• C++进阶语法——OOP(面向对象)【学习笔记(四)】


    1、C++ OOP面向对象开发

    1.1 类(classes)和对象(objects)

    简单介绍一下类:

    • C++ 类(classes)
      • 创建对象用的蓝图(blueprint)
      • ⽤户⾃定义的数据类型
      • 有成员属性(data)和成员⽅法(methods)
      • 可以隐藏属性和⽅法(private)
      • 可以提供公共接口(public)
      • 示例: Account, Student, std::vector, std::string

    简单介绍一下对象:

    • C++ 对象(objects):
      • 由类创建而来
      • 表示类的⼀个具体的实例(Instance)
      • 可以有很多个实例,每个都有独立的身份
      • 每个对象都可以使⽤类中定义的⽅法
    • Account对象示例:
      • Jobs、Alice 的account是Account类的实例
      • 每个实例都有它的余额、可以提现、存钱

    虽然 int 不是类,这里把它看成一个类,int 后面的 high_score 和 low_score 可以看成 int 类实例化后的对象;
    创建 Account 类后,实例化2个对象,分别是:jobs_account 和 alice_account;
    std是类,后面跟着的容器 vector 和字符串 string 是对象;

    如下图所示,

    在这里插入图片描述

    A、声明一个类:

    在这里插入图片描述

    B、创建类的对象:

    还可以根据类创建一个指针,并且在堆上使用关键字 new 动态分配内存空间,使用后再用 delete 删除释放内存空间,

    在这里插入图片描述

    一旦有了类的对象,就可以像 C++ 其他变量去使用

    在这里插入图片描述

    代码:

    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Account
    {
        // 属性
        string name {"None"};
        double balance {0.0};
    
        // 方法
        bool deposit(double amount); // 存款
        bool withdraw(double amount); // 取款
    };
    
    int main()
    {
        Account jobs_account;  // 初始化属性
        Account alice_account;  // 初始化属性
    
        Account accounts [] {jobs_account, alice_account}; // 数组
    
        vector<Account> accounts_vec {jobs_account}; // vector
        accounts_vec.push_back(alice_account);
    
        Account *p_account = new Account(); // 指针
        delete p_account;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    C、获取类的属性或方法:

    使用点操作符:

    在这里插入图片描述

    如果是⼀个指向对象的指针,可以解引用或者使⽤箭头操作符,
    需要注意: 使用 . 操作符的时候需要在前面加一个括号 () ,因为 . 操作符的优先级高于 * 解引用运算符的优先级,

    在这里插入图片描述

    代码:

    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Account
    {
    public:    
        // 属性
        string name {"None"};
        double balance {0.0};
    
        // 方法
        bool deposit(double amount){
            balance += amount;
            cout << name << "刚存入" << amount << "元,现在余额为" << balance << "元" << endl;
            return true;
        }; // 存款
        bool withdraw(double amount){
            if (balance >= amount){
                balance -= amount;
                cout << name << "刚取出" << amount << "元,现在余额为" << balance << "元" << endl;
                return true;
            } else {
                cout << name << "余额不足,取款失败" << endl;
                return false;
            }
        }; // 取款
    };
    
    int main()
    {
        Account jobs_account;
        jobs_account.name = "Jobs";
        jobs_account.balance = 1000.0;
    
        jobs_account.deposit(500.0);
    
        Account *alice_account = new Account();
        (*alice_account).name = "Alice";
        alice_account->balance = 2000.0;
    
        (*alice_account).deposit(1000.0);
        alice_account->withdraw(500.0);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    输出:

    Jobs刚存入500元,现在余额为1500元
    Alice刚存入1000元,现在余额为3000元
    Alice刚取出500元,现在余额为2500
    • 1
    • 2
    • 3
    1.2 public、private、protected访问权限
    • public
      • 可以被任何实体访问
    • private
      • 只能被本类的⽅法访问
    • protected
      • 可以被本类和子类(继承)的⽅法访问

    在这里插入图片描述

    在这里插入图片描述

    代码:

    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Account
    {
    private:    
        // 属性
        string name {"None"};
        double balance {0.0};
    
    public:
        // 方法
        bool deposit(double amount){
            balance += amount;
            cout << name << "刚存入" << amount << "元,现在余额为" << balance << "元" << endl;
            return true;
        }; // 存款
        bool withdraw(double amount){
            if (balance >= amount){
                balance -= amount;
                cout << name << "刚取出" << amount << "元,现在余额为" << balance << "元" << endl;
                return true;
            } else {
                cout << name << "余额不足,取款失败" << endl;
                return false;
            }
        }; // 取款
    };
    
    int main()
    {
        Account jobs_account;
        // jobs_account.name = "Jobs";  // 报错
        // jobs_account.balance = 1000.0;  // 报错
    
        // cout << jobs_account.name << "的余额为" << jobs_account.balance << "元" << endl;  // 报错
    
        jobs_account.deposit(500.0);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    输出:

    None刚存入500元,现在余额为500
    • 1
    1.3 实现成员方法

    在类中定义成员方法,适用于代码量较少的程序,

    在这里插入图片描述

    在类外面定义成员方法,适用于代码量较大的程序,

    在这里插入图片描述

    代码:

    编译器无法区分 set_name 函数是类内的函数还是其他函数,所以在 set_name 前面添加类名::,表示这是类的成员函数

    #include 
    #include 
    #include 
    
    using namespace std;
    
    class Account
    {
    private:
        // 属性
        string name {"None"};
        double balance {0.0};
    
    public:
        // 方法
    
        // 设置余额
        void set_balance(double amount){balance = amount;};
        // 获取余额
        double get_balance(){return balance;};
    
        // 设置名称
        void set_name(string name);
        // 获取名称
        string get_name();
    
        // 存款
        bool deposit(double amount);
        // 取款
        bool withdraw(double amount);
    };
    
    void Account::set_name(string name){
        this->name = name; // this指针指向当前对象
    }
    string Account::get_name(){
        return name;
    }
    bool Account::deposit(double amount){
        balance += amount;
        cout << name << "刚存入" << amount << "元,现在余额为" << balance << "元" << endl;
        return true;
    }
    bool Account::withdraw(double amount){
        if (balance >= amount){
            balance -= amount;
            cout << name << "刚取出" << amount << "元,现在余额为" << balance << "元" << endl;
            return true;
        } else {
            cout << name << "余额不足,取款失败" << endl;
            return false;
        }
    }
    
    int main()
    {
        Account alice_account;
        alice_account.set_name("Alice's account"); // 设置名称
        alice_account.set_balance(1000.0); // 设置余额
    
        cout << alice_account.get_name() << "的余额为" << alice_account.get_balance() << "元" << endl;
    
        alice_account.deposit(200.0);
        alice_account.withdraw(500.0);
        alice_account.withdraw(1500.0);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    输出:

    Alice's account的余额为1000元
    Alice's account刚存入200元,现在余额为1200元
    Alice's account刚取出500元,现在余额为700元
    Alice's account余额不足,取款失败
    
    • 1
    • 2
    • 3
    • 4

    上面的代码看着比较繁杂,我们可以用头文件和源文件编写。
    为了防止头文件被多次导入造成一些冲突,一般添加如下语句,关于头文件被多次导入造成一些冲突可参考这个链接:CSDN链接,例如创建 Account.h 头文件:

    #ifndef ACCOUNT_H
    #define ACCOUNT_H
    #endif // ACCOUNT_H
    
    • 1
    • 2
    • 3

    整个 Account.h 头文件代码如下,一般不在头文件写 using namespace std;,一般显式地使用 std:string

    #ifndef ACCOUNT_H
    #define ACCOUNT_H
    #include 
    
    class Account
    {
    private:
        // 属性
        std::string name {"None"};
        double balance {0.0};
    
    public:
        // 方法
    
        // 设置余额
        void set_balance(double amount){balance = amount;};
        // 获取余额
        double get_balance(){return balance;};
    
        // 设置名称
        void set_name(std::string name);
        // 获取名称
        std::string get_name();
    
        // 存款
        bool deposit(double amount);
        // 取款
        bool withdraw(double amount);
    };
    
    #endif // ACCOUNT_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    然后再根据头文件编写类的成员方法的定义,因为已经引入了头文件#include "Account.h",所以不需要再写 #include ,这里也是显示地引入 std::coutstd::endl

    #include "Account.h"
    #include 
    
    void Account::set_name(std::string name){
        this->name = name; // this指针指向当前对象
    }
    std::string Account::get_name(){
        return name;
    }
    bool Account::deposit(double amount){
        balance += amount;
        std::cout << name << "刚存入" << amount << "元,现在余额为" << balance << "元" << std::endl;
        return true;
    }
    bool Account::withdraw(double amount){
        if (balance >= amount){
            balance -= amount;
            std::cout << name << "刚取出" << amount << "元,现在余额为" << balance << "元" << std::endl;
            return true;
        } else {
            std::cout << name << "余额不足,取款失败" << std::endl;
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    最后是程序的 main.cpp 文件,直接编译 main.cpp 即可,

    #include 
    #include 
    #include 
    #include "Account.h" // 引入头文件
    
    using namespace std;
    
    
    int main()
    {
        Account alice_account;
        alice_account.set_name("Alice's account"); // 设置名称
        alice_account.set_balance(1000.0); // 设置余额
    
        cout << alice_account.get_name() << "的余额为" << alice_account.get_balance() << "元" << endl;
    
        alice_account.deposit(200.0);
        alice_account.withdraw(500.0);
        alice_account.withdraw(1500.0);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    输出:

    Alice's account的余额为1000元
    Alice's account刚存入200元,现在余额为1200元
    Alice's account刚取出500元,现在余额为700元
    Alice's account余额不足,取款失败
    
    • 1
    • 2
    • 3
    • 4
    1.4 构造函数(constructor)和 析构函数(destructor)
    1.4.1 构造函数(constructor)
    • 特殊的成员
    • 对象创建的时候自动调用
    • 适⽤于实例参数初始化
    • 函数名和类的名称⼀致
    • 无需设置返回类型
    • 可以被重载(overload)
    1.4.2 析构函数(destructor)
    • 特殊的成员⽅法
    • 函数名和类的名称⼀致,前⾯跟着⼀个 ~ 波浪符号
    • 对象销毁的时候自动调用
    • 没有参数,没有返回类型
    • 只有一个析构函数(不能重载)
    • 适用于释放内存等资源

    如果不手动创建构造函数和析构函数,那么C++会自动帮助创建构造函数和析构函数,只不过都是空的,

    构造函数(constructor)和析构函数(destructor):

    在这里插入图片描述

    构造函数在栈上创建,当程序运行结束时,他们各自的析构函数会被调用,所以会调用4次析构函数,

    在这里插入图片描述

    代码:

    #include 
    #include 
    #include 
    using namespace std;
    
    class Account
    {
    private:
        // 属性
        std::string name {"account"};
        double balance {0.0};
    public:
        void setName(string name){ this->name = name;}; // 设置名称
        // 构造函数
        Account(){
            cout << "没有参数的构造函数被调用" << endl;
        };
        Account(std::string name){
            cout << "带string name参数的构造函数被调用" << endl;
        };
        Account(double balance){ 
            cout << "带double balance参数的构造函数被调用" << endl;
        };
        Account(string name, double balance){ 
            cout << "带string name和double balance参数的构造函数被调用" << endl;
        };
        // 析构函数
        ~Account(){ 
            cout << name << " 的析构函数被调用" << endl;
        };
    };
    
    
    int main()
    {
        // 用{}表示作用域,{}内的程序运行后会调用析构函数
        {
            Account alice_account;
            alice_account.setName("Alice's account"); // 设置名称
        }
        // 出栈顺序:后入先出
        {
            Account jobs_account;
            jobs_account.setName("Jobs's account"); 
    
            Account bill_account("Bill's account");
            bill_account.setName("Bill's account"); 
    
            Account steve_account(1000.0);
            steve_account.setName("Steve's account"); 
        }
    
        Account *mark_account = new Account("Mark's account", 1000.0);
        mark_account->setName("Mark's account");
        delete mark_account;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    输出:

    没有参数的构造函数被调用
    Alice's account 的析构函数被调用
    没有参数的构造函数被调用
    带string name参数的构造函数被调用
    带double balance参数的构造函数被调用
    Steve's account 的析构函数被调用
    Bill's account 的析构函数被调用
    Jobs's account 的析构函数被调用
    带string name和double balance参数的构造函数被调用
    Mark's account 的析构函数被调用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    构造函数初始化列表:

    :name {name} 中,冒号后面的 name 类成员属性的 name,{ } 里面的 name 是函数的形参 name,

    在这里插入图片描述

    代码1(构造函数内部初始化参数):

    #include 
    #include 
    #include 
    using namespace std;
    
    class Account
    {
    private:
        // 属性
        std::string name {"account"};
        double balance {0.0};
    public:
        // 打印信息
        void printInfo();
        // 构造函数,初始化参数
        Account(string name, double balance);
       
    };
    
    void Account::printInfo(){
        cout << "name: " << name << ", balance: " << balance << endl;
    }
    // 构造函数内部初始化参数
    Account::Account(string name, double balance){
        this->name = name;
        this->balance = balance;
    }
    
    int main()
    {
    
        Account *mark_account = new Account("Mark's account", 1000.0);
        mark_account->printInfo(); // 打印信息
        delete mark_account;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    输出:

    name: Mark's account, balance: 1000
    
    • 1

    代码2(初始化列表初始化参数):

    #include 
    #include 
    #include 
    using namespace std;
    
    class Account
    {
    private:
        // 属性
        std::string name {"account"};
        double balance {0.0};
    public:
        // 打印信息
        void printInfo();
        // 构造函数,初始化参数
        Account();
        Account(string name);
        Account(string name, double balance);
       
    };
    
    void Account::printInfo(){
        cout << "name: " << name << ", balance: " << balance << endl;
    }
    
    // 构造函数内部初始化参数
    // Account::Account(){
    //     name = "None";
    //     balance = 0.0;
    // }
    // Account ::Account(string name){
    //     this->name = name;
    //     balance = 0.0;
    // }
    // Account::Account(string name, double balance){
    //     this->name = name;
    //     this->balance = balance;
    // }
    
    // 构造函数初始化列表
    Account::Account()
        :name{"none"}, balance{0.0}{
            
        }
    Account::Account(string name)
        :name{name}, balance{0.0}{
            
        }
    
    Account::Account(string name, double balance)
        :name{name}, balance{balance}{
            
        }
    
    int main()
    {
        Account alice_account;
        alice_account.printInfo(); // 打印信息
    
        Account jobs_account {"Jobs's account"};
        jobs_account.printInfo(); 
    
        Account bill_account {"Bill's account", 1000.0};
        bill_account.printInfo(); 
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    输出:

    name: none, balance: 0
    name: Jobs's account, balance: 0
    name: Bill's account, balance: 1000
    
    • 1
    • 2
    • 3
    1.5 代理构造函数(delegating constructor)
    • 重载的构造函数很相似
    • 冗余的代码可能会导致错误
    • C++ 允许使⽤代理构造函数
      • 在⼀个构造函数初始化列表中调⽤另⼀个构造函数,被调用的构造函数需要包含其他构造函数的参数,

    代码: 建议使用 debug 查看程序运行过程

    #include 
    #include 
    #include 
    using namespace std;
    
    class Account
    {
    private:
        // 属性
        std::string name {"account"};
        double balance {0.0};
    public:
        // 打印信息
        void printInfo();
        // 构造函数,初始化参数
        Account();
        Account(string name);
        Account(string name, double balance);
       
    };
    
    void Account::printInfo(){
        cout << "name: " << name << ", balance: " << balance << endl;
    }
    
    // 构造函数初始化列表
    Account::Account()
        :Account {"none",0}{
            
        }
    Account::Account(string name)
        :Account {name, 0}{
            
        }
    
    Account::Account(string name, double balance)
        :name{name}, balance{balance}{
            
        }
    
    int main()
    {
        Account alice_account;
        alice_account.printInfo(); // 打印信息
    
        Account jobs_account {"Jobs's account"};
        jobs_account.printInfo(); 
    
        Account bill_account {"Bill's account", 1000.0};
        bill_account.printInfo(); 
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    输出:

    name: none, balance: 0
    name: Jobs's account, balance: 0
    name: Bill's account, balance: 1000
    
    • 1
    • 2
    • 3
    1.6 拷贝构造函数(copy constructor)
    • 当对象被拷贝时,C++必须从已存在的对象复制出⼀个新的对象,
    • 何时使⽤拷贝构造函数?
      • 对象以值传递的方式传递给函数的参数(作参数)
      • 函数以值方式返回对象
      • 使用已存在的对象复制新的对象
    • 如果不提供自己写的copy constructor,编译器会⾃动⽣成⼀个(可能不符合要求),如果是指针,拷贝的是地址,所以编译器自动生成的拷贝构造函数是浅拷贝,可能在析构函数释放资源时会报错,

    A、拷贝构造函数——值传递

    在这里插入图片描述

    B、拷贝构造函数——以值方式返回

    以值方式返回拷贝构造函数返回的 an_account 副本

    在这里插入图片描述

    C、拷贝构造函数——使用已存在的对象复制新的对象

    在这里插入图片描述

    D、拷贝构造函数的声明

    • 首先拷⻉构造函数也是构造函数,所以函数的名称和类的名称一样,函数的参数列表只有一个参数,那就是对象,是使用引用的方式传递的,
    • 为什么使用引用和const?
      • 因为如果不使用引用,而是使用值传递的方式传递,又需要拷贝一份,这本来就是拷贝构造函数干的事情,就会陷入永无止境的递归了,
      • 使用const是不想对引用的对象进行改动,

    在这里插入图片描述

    在这里插入图片描述

    代码:

    #include 
    #include 
    #include 
    using namespace std;
    
    class Account
    {
    private:
        // 属性
        string name {"account"};
        double balance {0.0};
    public:
        string getName() {return name;} // 获取name
        double getBalance() {return balance;} // 获取balance
        // 构造函数
        Account(string name = "none", double balance = 0.0);
        // 析构函数
        ~Account();
        // 拷贝构造函数
        Account(const Account &source);
    };
    
    Account::Account(string name, double balance)
        :name {name}, balance {balance}
        {
            cout << "2个参数的构造函数被调用,name:" << name << endl;
        }
    
    Account::~Account()
    {
        cout << "析构函数被调用,name:" << name << endl;
    }
    
    // 拷贝构造函数:根据已存在对象的属性来更新新对象的属性(name,balance)
    Account::Account(const Account &source)
        :name {source.name}, balance {source.balance} // 初始化列表
        {
            cout << "拷贝构造函数被调用,是" << source.name << "的拷贝" << endl;
        }
    // 打印账户信息
    void printAccountInfo(Account acc)
    {
        cout << acc.getName() << "的余额是:" << acc.getBalance() << endl;
    }
    int main()
    {
        // 1.值传递的方式给函数传递参数
        // Account alice_account;
        // printAccountInfo(alice_account);
        
        // 2.基于已存在的对象创建新的对象
        Account alice_account {"Alice's account", 1000.0};
        Account new_account {alice_account}; // 拷贝构造函数被调用
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    输出:

    2个参数的构造函数被调用,name:Alice's account
    拷贝构造函数被调用,是Alice's account的拷贝
    析构函数被调用,name:Alice's account
    析构函数被调用,name:Alice's account
    
    • 1
    • 2
    • 3
    • 4
    1.7 浅拷贝(shallow copy)和深拷贝(deep copy)
    • 如果不提供自己写的copy constructor,编译器会⽣成默认的
    • 将⽬标对象的值逐个拷⻉过来;
    • 如果是指针,拷贝的是值(即指针变量的值,是指向的地址),⽽不是指向的对象(指向的对象称为浅拷贝)
      • 在析构函数中释放内存时,其他对象中的指针可能还在指向被释放的资源,在析构函数释放内存资源时可能会报错,例如下面的代码,如果使用编译器默认的拷贝构造函数,在进行析构函数释放内存资源时可能会报错

    代码:

    #include 
    #include 
    #include 
    using namespace std;
    
    class Account
    {
    private:
        // 属性
        string name {"account"};
        double *balance {nullptr};
    public:
        double get_balance() {return *balance;}; // 获取余额
        string get_name() {return name;}; // 获取名字
    
        // 构造函数
        Account(string name = "none", double balance = 0.0); // 有默认参数的构造函数
        // 拷贝构造函数
        Account(const Account &source);
        // 析构函数
        ~Account();
    
        
    };
    Account::Account(string name, double balance)
        {
            this->name = name;
            this->balance = new double {balance}; // 堆上分配内存
            cout << "2个参数的构造函数被调用,name: " << name << endl;
        }
        
    // 拷贝构造函数    
    Account::Account(const Account &source)
        :Account {source.name, *source.balance} // 代理构造函数
        {
            cout << "拷贝构造函数被调用,是" << source.name << "的拷贝" << endl;
        } 
        
    Account::~Account(){
        if (balance != nullptr)
            delete balance; // 释放内存
        cout << "析构函数被调用,name: " << name << endl;
    }
    
    int main()
    {
        // 演示浅拷贝和深拷贝
        Account alice_account {"Alice", 1000.0};
        Account new_account {alice_account}; // 拷贝构造函数被调用
        // cout << new_account.get_balance() << endl; // 1000.0
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    如果使用编译器默认的拷贝构造函数,在进行 alice_account 析构函数释放内存时会报错,如下图,

    在这里插入图片描述

    1.7.1 浅拷贝(shallow copy)

    如下图,alice_account 浅拷贝到 new_account,编译器会默认创建一个拷贝构造函数,它是逐个元素按值拷贝的如果是指针,则是拷贝的地址。当调用析构函数的时候,由于后进先出的原则,new_account 上的地址被释放后,alice_account 的地址也会被释放,但地址已经被释放,所以会造成堆空间重复释放的问题,导致程序报错,

    在这里插入图片描述

    编译器默认生成的拷贝构造函数如下:

    在这里插入图片描述

    1.7.2 深拷贝(deep copy)

    在函数内部,在堆上分配一个新的 double 类型的内存空间,初始化为1000,并且把新的内存空间给1000,

    在这里插入图片描述

    代码:

    #include 
    #include 
    #include 
    using namespace std;
    
    class Account
    {
    private:
        // 属性
        string name {"account"};
        double *balance {nullptr};
    public:
        double get_balance() {return *balance;}; // 获取余额
        string get_name() {return name;}; // 获取名字
    
        // 构造函数
        Account(string name = "none", double balance = 0.0); // 有默认参数的构造函数
        // 拷贝构造函数
        Account(const Account &source);
        // 析构函数
        ~Account();
    
        
    };
    Account::Account(string name, double balance)
        {
            this->name = name;
            this->balance = new double {balance}; // 堆上分配内存
            cout << "2个参数的构造函数被调用,name: " << name << endl;
        }
    // 拷贝构造函数    
    Account::Account(const Account &source)
        :Account {source.name, *source.balance} // 代理构造函数,深拷贝
        {
        	// this->name = source.name;
        	// this->balance = source.balance  // 浅拷贝
            // this->balance = new double {*source.balance}; // 深拷贝
            cout << "拷贝构造函数被调用,是" << source.name << "的拷贝" << endl;
        } 
        
    Account::~Account(){
        if (balance != nullptr)
            delete balance; // 释放内存
        cout << "析构函数被调用,name: " << name << endl;
    }
    
    int main()
    {
        // 演示浅拷贝和深拷贝
        Account alice_account {"Alice", 1000.0};
        Account new_account {alice_account}; // 拷贝构造函数被调用
        // cout << new_account.get_balance() << endl; // 1000.0
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    输出:

    2个参数的构造函数被调用,name: Alice
    2个参数的构造函数被调用,name: Alice
    拷贝构造函数被调用,是Alice的拷贝
    析构函数被调用,name: Alice
    析构函数被调用,name: Alice
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1.8 在类中使用const
    • 常函数
      • 函数名称后加const
      • 函数体内不可以修改类成员属性
    • 常对象
      • 声明对象时前⾯加const
      • 不可以修改常对象的成员属性
      • 不能调用普通的成员方法,只能调⽤常函数

    代码:

    #include 
    #include 
    #include 
    using namespace std;
    
    
    class Account
    {
    private:
        
        double balance {0.0};
    
    public:
        string name {"account"};
    
        void set_new_name(string new_name) const{ // 修改名字,加入const就不可以修改类成员属性,
            // name = new_name;
        }
        string get_name() const{ // 获取名字
            return name;
        }
        // 构造函数
        Account(string name = "none", double balance = 0.0);
        ~Account();
    };
    
    Account::Account(string name, double balance)
        : balance{balance} ,name{name}{
            cout << "构造函数" << endl;
    }
    
    Account::~Account()
    {
        cout << "析构函数" << endl;
    }
    
    
    int main()
    {
        // 常函数
        // Account alice_account {"Alice", 1000.0};
        // alice_account.set_new_name("Alice2"); // 修改名字
        // cout << alice_account.get_name() << endl; // Alice2
    
        // 常对象
        const Account bob_account {"Bob", 2000.0};
        // bob_account.name = "Bob2"; // 修改名字
        cout << bob_account.get_name() << endl; // Bob2
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    输出:

    构造函数
    Bob
    析构函数
    
    • 1
    • 2
    • 3
    1.9 在类中使用static
    • 静态成员变量
      • 所有对象共享同⼀份数据(数据共享)
      • 在类中声明,类外初始化
    • 静态成员函数
      • 所有对象共享同⼀个函数
      • 只能访问静态成员变量

    在这里插入图片描述

    代码:

    #include 
    #include 
    #include 
    using namespace std;
    
    class Account
    {
    private:
        // 属性
        string name {"account"};
        double balance {0.0};
        static int num_accounts;
    public:
        static int get_num_accounts();
        Account(string name, double balance);
        ~Account();
    };
    
    int Account::num_accounts {0};
    
    Account::Account(string name, double balance)
        :name{name}, balance{balance}{
            num_accounts++;
        }
    
    Account::~Account()
    {
        num_accounts--;
    }
    
    int Account::get_num_accounts() // 不需要使用static关键字
    {
        // 静态成员方法只能访问静态成员变量
        // name = "test";
        return num_accounts;
    }
    int main()
    {
        cout << Account::get_num_accounts() << endl;
        Account alice_account {"Alice's account", 1000.0};
        cout << alice_account.get_num_accounts() << endl;
        Account bob_account {"Bob's account", 2000.0};
        cout << Account::get_num_accounts() << endl;
        
        {
            Account test_account {"test", 100.0};
            cout << Account::get_num_accounts() << endl;
        }
        cout << Account::get_num_accounts() << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    输出:

    0
    1
    2
    3
    2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1.10 struct 和 class区别

    唯一区别在于 class 成员权限默认是 private,而 struct 的成员权限默认是 public,

    在这里插入图片描述

    如果本博客对你有帮助,请点赞收藏吧😊😊😊

  • 相关阅读:
    Relation Extraction as Open-book Examination: Retrieval-enhanced Prompt Tuning
    java面试题总结3
    Java网络编程
    FPGA GTH 全网最细讲解,aurora 8b/10b编解码,HDMI视频传输,提供2套工程源码和技术支持
    在linux上做移动开发必须知道这五个
    JG/T 543-2018 铝塑共挤门窗检测
    国产操作系统之凝思磐石安装
    如何解决缓存一致性问题
    Vue3.0项目——打造企业级音乐App(二)图片懒加载、v-loading指令的开发和优化
    异硫氰酸荧光素标记PCL载药纳米粒|FITC-PCL|PCL-FITC|FITC-SS-PEG-PCL(齐岳)
  • 原文地址:https://blog.csdn.net/qq_23022733/article/details/133932495