• C++ 基础与深度分析 Chapter11 类与面向对象编程(析构与复制成员函数、字面值类、成员指针与bind交互)


    析构函数

    在这里插入图片描述
    构造函数是把对象构造出来,析构函数就是把对象销毁掉。
    但是注意内存的分配与释放,和构造函数与析构函数无关。

    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    
    struct Str
    {  
        Str()
        {
            cout << "Constructor is called" << endl;
        }
        // 析构函数,当对象销毁时会被调用
        ~Str()
        {
            cout << "Destructor is called" << endl;
        }
        int val = 3; 
        std::string a = "abc"; 
        int* ptr;
    };
    
    
    int main()
    {
        Str m;
        Str m2;
        Str m3;
        // c++构造顺序与销毁顺序是相反的,析构函数会自动调用
        Str* m_ptr = new Str(); // 这个对象不会被销毁,new开辟内存,然后调用构造函数
        delete m_ptr; // 释放内存,调用析构函数,销毁对象
    }
    
    • 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 <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    
    class Str
    {  
    public:
        Str() :ptr(new int()) {} // 构造函数
        ~Str() {delete ptr; } // 析构函数
    
        Str(const Str& val) // 拷贝构造函数,有析构函数一般都要有拷贝构造函数
        // 这样释放b之后,再释放a,就不会同一块内存释放2次了
            : ptr(new int())
        {
            *ptr = *(val.ptr);
        }
    
        //移动构造
        Str(Str&& val)
            : ptr(val.ptr)
        {
            val.ptr = nullptr;
        }
    
        Str& operator= (const Str& val) // 赋值拷贝,把ptr指向的内容进行赋值
        {
            *ptr = *(val.ptr);
            return *this;
        }
    
        int& Data()
        {
            return *ptr;
        }
    
    private:
        int* ptr; // &(a.ptr) = 1234 a.ptr = 5678 5678是a.ptr存的地址; 
        // &(b.ptr) = 2345 b.ptr = 5678, 拷贝构造,b(a);
    };
    
    
    int main()
    {
        Str a;
        a.Data() = 3;
        cout << a.Data() << endl;
        Str b(a); // 先销毁b,这样5678这块内存已经销毁掉了,我们在销毁a存的5678时,已经没有了,销毁了2次
        b = a; // 拷贝赋值
        Str c = std::move(a); // 移动构造,移动构造不开辟内存资源,是把a.ptr给了c.ptr,清空a.ptra.ptr
       
    }
    
    • 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

    delete关键字

    default 关键字 ,只对特殊成员函数有效。但是,delete对所有的函数都有效。
    在这里插入图片描述
    通常来讲,我们会给析构函数加delete。delete就是不让析构。构造函数一般可以加delete,就是不让构造。

    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    
    class Str
    {  
    public:
        Str() :ptr(new int()) {} // 构造函数
        // Str() = delete; // 构造函数+delete 报错 error: call to deleted constructor of 'Str'
        // ~Str() {delete ptr; } // 析构函数
        ~Str() = delete; // 析构函数+delete 报错 attempt to use a deleted function
    
        Str(const Str& val) // 拷贝构造函数,有析构函数一般都要有拷贝构造函数
        // 这样释放b之后,再释放a,就不会同一块内存释放2次了
            : ptr(new int())
        {
            *ptr = *(val.ptr);
        }
    
        //移动构造
        Str(Str&& val)
            : ptr(val.ptr)
        {
            val.ptr = nullptr;
        }
    
        Str& operator= (const Str& val) // 赋值拷贝,把ptr指向的内容进行赋值
        {
            *ptr = *(val.ptr);
            return *this;
        }
    
        int& Data()
        {
            return *ptr;
        }
    
    private:
        int* ptr; // &(a.ptr) = 1234 a.ptr = 5678 5678是a.ptr存的地址; 
        // &(b.ptr) = 2345 b.ptr = 5678, 拷贝构造,b(a);
    };
    
    
    int main()
    {
        Str a;
        a.Data() = 3;
        cout << a.Data() << endl;
        Str b(a); // 先销毁b,这样5678这块内存已经销毁掉了,我们在销毁a存的5678时,已经没有了,销毁了2次
        b = a; // 拷贝赋值
        Str c = std::move(a); // 移动构造,移动构造不开辟内存资源,是把a.ptr给了c.ptr,清空a.ptra.ptr
    }
    
    • 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

    在这里插入图片描述
    通常不要给移动构造和移动赋值引入delete限定符。
    如果只需要拷贝行为,那么引入拷贝构造和拷贝赋值即可。
    如果不需要拷贝行为,那么拷贝构造和拷贝赋值只需要一个声明+delete限定符即可。
    不要为移动构造或者移动赋值引入delete。
    在这里插入图片描述

    字面值类

    在这里插入图片描述
    可以构造编译期的抽象数据类型。
    在这里插入图片描述

    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    
    class Str
    { 
    public:
        constexpr Str(int val)
            : x(val)
        {
        }
    private:
        int x;
    };
    
    // constexpr int a = 3; // 编译期的常量
    // int b = 5; // 运行期变量
    
    int main()
    {
        constexpr Str a(100);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述
    字面值类是比较特殊的数据结构,可以在编译期使用。但是使用的范围比较窄。

    成员指针

    在这里插入图片描述

    #include <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    
    class Str
    { 
    public:
        int x;
    };
    
    
    int main()
    {
        // 声明成员指针的前提是必须有这个类,可以是定义或者声明
        int Str::*ptr; //函数成员的指针,指向Str域内的int成员
        void (Str::* ptr_fun) (); // 函数成员指针,指向Str域内的void 0参数的函数
        int Str::* ptr1 = &Str::x; // 成员指针的赋值
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    bind交互

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    目标检测YOLO实战应用案例100讲-水下机器人视域中小目标检测(中)
    SpringBoot整合Junit
    Linux :realpath 命令
    折柱混合图的多种用法
    小程序首页搭建
    Dynamic DataSource 多数据源配置【 Springboot + DataSource + MyBatis Plus + Druid】
    【Java】-【使用smtp协议发邮件】
    函数式编程之递归的几种方式
    找工作----C++面试题库
    ABB机器人欧拉角与四元数的相互转化以及旋转矩阵的求法
  • 原文地址:https://blog.csdn.net/weixin_43716712/article/details/125551411