《C++程序设计》笔记p4

C++ 类中的四个默认成员函数

  1. 构造函数

  2. 拷贝构造函数

  3. 析构函数

  4. 赋值运算符重载

    class 类名{
    public:
    类名(void);
    类名(const 类名 &);
    ~类名(void);
    类名 & operator =(const 类名 &);
    };

赋值运算符重载:

作用:代替默认的赋值运算符成员函数来控制对象复制的过程,从而避免复制过程中的浅拷贝。

调用时机: 在对象已经创建,并在后续运行阶段对其值进行修改时调用。

语法:

复制代码
class 类名{
    类名 & operator = (const 类名 & src) {
        ...
        return *this;
    }
};

示例:

复制代码
#include <iostream>
#include <stdlib.h>
#include <string.h>

using namespace std;

// 定义一个MyString类,使用动态数组来存储字符信息
class MyString {
    public:
        // 将字符串初始化成 n个内容为 c的字符
        MyString(char c, int n) {
            cout << "MyString(" << c << "," << n<<")\n";
            data = (char*)malloc(n+1);
            for (int i = 0; i < n; i++) {
                data[i] = c;
            }
            data[n] = '\0';
        }
        MyString(const char * p=""):data(NULL) {
            cout << "MyString(" << p << ")\n";
            // 计算传入参数的长度
            int str_len = strlen(p);
            data = (char*)malloc(str_len+1);
            strcpy(data, p);
        }
        // 拷贝构造函数,对指针执向的内容进行深度复制
        MyString(const MyString & src) {
            cout << "MyString(const MyString& src.data:" << src.data << ")\n";
            // 求源对象字符的长度
            int str_len = strlen(src.data);
            data = (char*) malloc(str_len+1);
            strcpy(data, src.data);
        }
        // 析构函数
        ~MyString(){
            cout << "~MyString(" << data << ")" << endl;
            free(data);
        }
        MyString& operator=(const MyString &src) {
            cout << "operator = 被调用" << endl;
            // (*this).copy(src);
            this->copy(src);
            // 释放自己原有的内存
            // free(data);
            // int str_len = strlen(src.data);
            // data = (char*)malloc(str_len+1);
            // strcpy(data, src.data);
            return *this;
        }
        const char * c_str(void) {
            return data;
        }
    private:
        char * data;
    public:
        // 要求不能有内存泄漏
        void copy(const char * content) {
            // 释放自己原有的内存
            free(data);
            int str_len = strlen(content);
            data = (char*)malloc(str_len+1);
            strcpy(data, content);
        }
        inline void copy(const MyString & src){
            // 释放自己原有的内存
            free(data);
            int str_len = strlen(src.data);
            data = (char*)malloc(str_len+1);
            strcpy(data, src.data);
        }
};

int main(int argc, char * argv[]) {
    MyString s1 = "zhangsan";
    MyString s2 = s1;
    MyString s3;
    // MyString s2;
    // s2.copy(s1);
    //调用MyString& MyString::operator=(const MyString&);
    s3 = s2 = s1;
    cout << "s1:" << s1.c_str() << endl; // zhangsan
    cout << "s2:" << s2.c_str() << endl; // zhangsan
    cout << "s3:" << s3.c_str() << endl; // zhangsan
    s2.copy("lisi");
    cout << "s2:" << s2.c_str() << endl; // lisi 

    cout << "程序结束!" << endl;
    return 0;
}

构造函数调用的时机

  1. 栈、堆以及全局对象的对象创建。
  2. 函数参数传值(传引用和传址不会创建新对象)
  3. 函数返回值(非引用和传址是不会创建新对象)

示例:

复制代码
#include <iostream>
#include <stdlib.h>
#include <string.h>

using namespace std;

class MyString2 {
    public:
        MyString2(const string & s="") {
            cout << "MyString2(" << s << ")" << endl;
            data = s;
        }
        // 拷贝构造函数,对指针执向的内容进行深度复制
        MyString2(const MyString2 & src) {
            cout << "拷贝构造MyString2()" << src.data << endl; 
            data = src.data;
        }
        // 析构函数
        ~MyString2(){
            cout <<"~MyString2(" << data << ")" << endl;
        }
        MyString2& operator=(const MyString2 &src) {
            cout << "operator = 被调用:"<< data << endl;
            data = src.data;
            return *this;
        }
    private:
        string data;
};

MyString2 &myfun(MyString2 & arg1) {
    return arg1;
}

int main(int argc, char * argv[]) {
    MyString2 s1("A1");
    // MyString2 s2;
    MyString2 &s2 = myfun(s1);
    cout << "程序结束!" << endl;
    return 0;
}

类的声明方式

  1. 仅声明类
  2. 声明类,同时声明成员变量和成员函数.

仅声明类

复制代码
class 类名;

声明类,同时声明成员变量和成员函数。

复制代码
class 类名 {
    public:
        类名(形式参数列表);
        返回类型 成员函数名(形式参数列表);
        ...
    private:
        变量类型1 成员变量1;
        变量类型2 成员变量2;
        ...
};

友元声明

友元声明是指让声明的一个函数、类或成员函数可以访问该类的私有和保护成员。

作用:让全局函数、其他类或其他类的成员函数可以访问该类的私有和保护成员。

关键字 friend

友元声明的分类

  1. 友元函数
  2. 友元成员函数
  3. 友元类

语法:

复制代码
class 类名 {
   // 友元函数的声明语法
   friend 返回类型 友元函数名(形式参数列表);
   // 友元成员函数的声明语法
   friend 返回类型 其他类::类名友元函数名(形式参数列表);
   // 友元类的声明语法
   friend class 其他类;
};

示例代码

复制代码
#include <iostream>

using namespace std;

class Children; // 类声明
class Parent{
    public:
        Parent(const string & n, int a)
            : name(n),age(a),money(0){}
        void work(int m);
        void showInfo(void);
        void giveMoneyTo(Children & c, int m); // 给孩子钱
    private:
        string name;
        int age;
        int money;
    // 声明show_money函数可以访问本类的所有成员
    friend void show_money(const Parent &);
    friend class Children;
};

class Children {
    public:
        Children(const string & n, int a)
            : name(n),age(a),money(0){}
        void showInfo(void);
        void borrowTo(Parent & p, int m);
    private:
        string name;
        int age;
        int money;
// 仅声明Parent类的giveMoneyTo函数可以访问本类的所有成员
        friend void Parent::giveMoneyTo(Children & c, int m);
};

void Parent::work(int m) {
    money += m;
}

void Parent::showInfo(void) {
    cout << age << "岁的" << name << "有钱" << money
        << "元" << endl;
}

void Children::showInfo(void) {
    cout << age << "岁的" << name << "有钱" << money
        << "元" << endl;
}

void Parent::giveMoneyTo(Children & c, int m) {
    if (m > this->money) {
        cout << "钱不够" << endl;
        return;
    }
    this->money -= m;
    c.money += m;
}

void Children::borrowTo(Parent & p, int m) {
    if (m > this->money) {
        cout << "本宝宝没钱,不借" << endl;
        return;
    }
    this->money -= m;
    p.money += m;
}

// 全局函数,用来打印家长的钱数;
void show_money(const Parent & p) {
    cout << p.age << "岁的" << p.name << "有钱"
        << p.money << "元" << endl;
}

int main(int argc, char * argv[]) {
    Parent p("老张", 31);
    Children c("小张", 12);
    p.work(1000);
    show_money(p);
    p.giveMoneyTo(c, 700);
    p.showInfo();
    c.showInfo();
    c.borrowTo(p, 200);
    p.showInfo();
    c.showInfo();

    return 0;
}

常成员函数

作用:声明一个成员函数为常成员函数,常成员函数在修改该类成员变量时将会报错(在编译阶段报错),且不能调用 常成员函数

语法:

复制代码
class 类名{
    返回类型 成员函数名(形式参数列表) const {...}
};

示例代码

复制代码
#include <iostream>

using namespace std;

class Parent{
    public:
        Parent(const string & n, int a)
            : name(n),age(a),money(0){}
        void work(int m);
        void showInfo(void) const;
    private:
        string name;
        int age;
        int money;
};


void Parent::work(int m) {
    money += m;
}

void Parent::showInfo(void) const {
    cout << age << "岁的" << name << "有钱" << money
        << "元" << endl;
    // money = 0; // 报错
    // this->work(100); // 报错
}

int main(int argc, char * argv[]) {
    Parent p("老张", 31);
    p.work(1000);
    p.showInfo();
    return 0;
}

静态成员函数和静态成员变量

静态成员变量

是类内的变量,他的作用域属于类。

声明的语法:

复制代码
class 类名{
    访问说明符:
        static 变量类型 变量名;
};

初始化的语法

复制代码
变量类型 类名::变量名[ = 初始值];

静态成员变量的使用方法

复制代码
类名::变量名
对象名.变量名

示例:

记录一个类创建对象的个数;

复制代码
#include <iostream>
#include <stdlib.h>
#include <string.h>

using namespace std;

class Human {
    public:
        static int total_count;
        Human(const string & n) : name(n){
            cout << "Human(" << name << ")\n";
            total_count++;
        }
        ~Human(){
            cout << "~Human(" << name << ")\n";
            total_count--;
        }
    private:
        string name;
};

int Human::total_count = 0;

int main(int argc, char * argv[]) {
    Human zhang3("张三");
    do {
        Human li3("李四");
    } while(0);
    // 程序运行到此处能否指导此时创建了几个Human对象?
    cout << "人口总数" << Human::total_count << endl;
    cout << "人口总数" << zhang3.total_count << endl;

    cout << "程序结束!" << endl;
    return 0;
}

静态成员函数

是类的函数,他只能访问类的静态成员变量,不能访问对象的成员变量。

说明:

  • 静态成员函数 没有this指针,不能访问对象的成员变量。
  • 静态成员函数的作用域属于据类。

静态成员函数 的定于的语法

复制代码
class 类名{
   public:
      // 类内声明,类内实现。
      static 返回类型 函数名1(形式参数列表) {};
      // 类内声明,类外实现。
      static 返回类型 函数名2(形式参数列表);
}

类外实现的语法

复制代码
返回类型 类名::函数名2(形式参数列表) { ... }

调用方法

复制代码
类名::函数名2(实际参数列表)
对象.函数名2(实际参数列表)

面相对象编程语言的特征

  • 封装
  • 继承
  • 多态

封装

封装是指隐藏对象的实现细节,让使用者不关心这些细节。从而保证数据的安全性。

使用者只需要调用简单的接口的即可使用。

C++ 通过访问说明符实现封装。

访问说明符

  • public
  • protected
  • private
访问说明符 成员函数 子类成员函数 其他
public 可以访问 可以访问 可以访问
protected 可以访问 可以访问 无法访问
private 可以访问 无法访问 无法访问

继承

继承是允许一个类(子类或派生类)基于另外一个类(父类或基类)创建,并继承基类的成员变量和成员函数。

继承的作用:

  • 将公有部分放入基类,实现代码共享。
  • 子类可以在原有的基础上添加新的功能。

继承的语法:

复制代码
class 子类名:继承方式 基类名 {
   ...
};

继承方式

  • 共有继承 public
  • 保护继承 protected
  • 私有继承 private

继承的内存结构

显式调用父类的构造函数的语法

复制代码
class 子类名:继承方式 基类名 {
     public:
          子类名(形参列表): 基类名(实参),成员变量1(实参),成员变量2(实参){...}
};

示例:

复制代码
#include <iostream>
#include <stdlib.h>
#include <string.h>

using namespace std;

// 点类(描述一个点的位置,面积,周长等信息
class Point {
    public:
        Point(int ax=0, int ay=0):x(ax), y(ay) {
            cout << "Point(" << x << "," << y << ")\n";
        }
        void info(void) {
            cout << "点(" << x << "," << y << ")\n";
        }
        void moveTo(int new_x, int new_y) {
            x = new_x; y = new_y;
        }
        float getArea(void) {
            return 0;
        }
        float getLength(void) {
            return 0;
        }
    public:
        int x;
        int y;
};
// 定义一个圆的类(位置和半径)
class Circle : public Point {
    public:
        Circle(int ax, int ay, int radius)
            : Point(ax, ay), r(radius) {
            //  x = ax; y = ay; r = radius;
            cout << "Circle(" << x << "," << y << "," << r << ")\n";
        }
        // void info(void) {
        //     cout << "圆(" << x << "," << y << "," << r << ")\n";
        // }
        // float getArea(void) {
        //     return 0;
        // }
        // float getLength(void) {
        //     return 0;
        // }
    public:
        int r; // 半径
};

int main(int argc, char * argv[]) {
    Point p1(3, 5);
    Circle c1(4, 6, 10);

    cout << "sizeof(p1)" << sizeof(p1) << endl;
    cout << "sizeof(c1)" << sizeof(c1) << endl;
    c1.info();
    p1.info();
    p1.moveTo(100, 105);
    // 子类对象不存在moveTo,则调用父类的方法
    c1.moveTo(200, 210);
    p1.info();
    c1.info();
    cout << "程序结束!" << endl;
    return 0;
}

访问权限

基类访问权限 继承方式 本类访问权限
public public public
protected public protected
private public 不可访问
public protected protected
protected protected protected
private protected 不可访问
public private private
protected private private
private private 不可访问
相关推荐
什么半岛铁盒2 小时前
C++项目:仿muduo库高并发服务器--------Any类的实现
linux·服务器·数据库·c++·mysql·github
Dream achiever2 小时前
7.WPF 的 TextBox 和 TextBlock 控件
开发语言·c#·wpf
玖笙&2 小时前
✨WPF编程基础【1.1】:XAML文档框架
c++·visualstudio·wpf
love530love2 小时前
EPGF 架构为什么能保持长效和稳定?
运维·开发语言·人工智能·windows·python·架构·系统架构
吃不胖没烦恼2 小时前
Alibaba Cloud Linux 3 +Docker 部署 ThinkPHP6 (宝塔环境)
linux·运维·docker
l1t2 小时前
测试duckdb的C插件模板的编译加工和加载
c语言·开发语言·数据库·插件·duckdb
峥嵘life2 小时前
Android16 应用代码新特性
java·开发语言·学习·安全
浮尘笔记3 小时前
Go-Zero API Handler 自动化生成与参数验证集成
开发语言·后端·golang
运维帮手大橙子3 小时前
算法相关问题记录
算法