• 复制控制 copy control(非平凡的类)


    C++自学精简教程 目录(必读)

    参考文章 :

    自定义类型 struct/class

    对象之间赋值

    1 非平凡的类

    当一个类有指针成员的时候,这样的类叫非平凡的类

    这个指针成员一般都是在管理着堆上的内存(比如,堆变量的地址)。

    1.1 非平凡的类构造函数与析构函数的执行

    下面的代码展示了有指针成员的对象管理动态内存的过程:

    1. #include
    2. using namespace std;
    3. class Student
    4. {
    5. public:
    6. int* m_age;//指针成员:管理动态内存
    7. //构造函数中申请堆内存
    8. Student() :m_age(new int(18)) //3 执行构造函数
    9. {
    10. }
    11. ~Student()//5 析构函数释放堆内存
    12. {
    13. delete m_age;
    14. }
    15. };
    16. int main()
    17. {
    18. //1 main函数开始执行
    19. Student stu;//2 在栈上创建新对象stu(分配内存),接下来调用构造函数
    20. return 0;
    21. }//4 stu对象超出作用域,接下来调用析构函数

    1,2(栈对象stu创建,内部有一个栈指针变量m_age)

    3 构造函数执行,在堆上申请一个无名整形变量,赋值18,并把地址赋值给m_age

    4 构造函数执行完毕,stu初始化完成

    5 析构函数执行,堆变量被释放,m_age仍然指向那个地方(这时候解引用会引发未定义的错误)

    析构函数执行完成之后,栈变量和main函数,栈空间,堆空间全部一起被操作系统收回,程序结束

     

    2(默认)浅拷贝的问题

    像上面的m_age这种成员变量,在对象之间的赋值(浅拷贝)的时候,就会导致两个对象的指针成员都指向了同一个地址。

    1. Student stu1;
    2. Student stu2;//(1)
    3. stu2 = stu1;//(2)出问题了!!!两个指针成员同时指向了一个堆变量

    对象stu1,stu2各自创建好之后

    两个对象的指针同时指向一个堆变量,stu1,stu2析构的时候分别释放一次,程序会挂掉

    因为一个对象要析构的时候会释放这个地址的内存。另一个对象释放的时候也要释放这个内存。

    这会导致两次释放同一个内存,第一次释放内存的时候内存就归还给系统了。

    当你第二次再释放这个内存的时候,你在释放不再归你管的内存,操作系统会把你的程序杀掉。运行的时候你的程序就会闪退。

    3 深拷贝(另起炉灶做同样的饭菜)

    为了避免上面的问题,那解决的办法就是(深拷贝)。

    一个对象在被赋值的时候,另开炉灶,在堆上再重新开辟一个属于自己的内存,把数值拷贝进去。

    这样两个对象各自管理属于自己的内存,就不会有问题。这就是深拷贝

    拷贝对象主要发生在两个地方拷贝构造函数,赋值

    3.1 拷贝构造函数copy constructor

    非平凡类的拷贝构造函数

    拷贝构造函数是构造函数的一个,这个函数的参数是该类型的另一个对象。

    1. class Student
    2. {
    3. public:
    4. int* m_age;//指针成员:管理动态内存
    5. //构造函数中申请堆内存
    6. Student() :m_age(new int(18))
    7. {
    8. }
    9. //copy constructor 拷贝构造函数(函数名为类名,参数为同类型的另一个对象
    10. Student(const Student& stuFrom);
    11. ~Student()
    12. {
    13. delete m_age;//析构函数释放堆内存
    14. }
    15. };

    拷贝构造函数的实现(深拷贝)

    1. //拷贝构造函数,新开辟一个堆变量,用m_age管理,数值设置为from的m_age管理的对变量的数值
    2. Student::Student(const Student& from):m_age(new int(*from.m_age))
    3. {
    4. }

    拷贝构造函数执行的时机:拷贝构造函数(浅拷贝)

    (1)用一个对象构造另一个对象

    1. Student stu1;//(1)
    2. Student stu2(stu1);//(2)

    (1)创建stu1对象的时候

    (2)创建stu2的时候:指针指向各自的堆变量

    (2)函数传值

    1. void test_function(Student s)//s的创建会调用拷贝构造函数

    (3)函数返回值类型

    1. Student test_function(void)// return语句执行的时候会调用拷贝构造函数

    3.2 赋值操作符

    赋值操作符和拷贝操作符类似,属于深拷贝。

    1. Student stu1;
    2. *stu1.m_age = 18;
    3. Student stu2;
    4. *stu2.m_age = 19;
    5. stu2 = stu1;//赋值操作符
    6. class Student{
    7. Student& operator=(const Student& stuFrom);
    8. };
    9. // stu2
    10. Student& Student::operator=(const Student& stuFrom){
    11. if(this == &stuFrom)//自己赋值给自己,直接返回自己
    12. {
    13. return *this;
    14. }
    15. *m_age = *stuFrom.m_age;//深拷贝,将存储的数据拷贝,而不是直接地址拷贝
    16. return *this;
    17. }

    未执行赋值之前两个对象的数据

    执行赋值之后,stu2的age从19重新被覆盖成18

    4 复制控制

    复制控制,专门指上文提到的深拷贝。如需要专门重写拷贝构造函数,赋值操作符来加以管理。

    1 C++提供三个默认实现:默认构造函数 default constructor;拷贝构造函数 copy constructor; 析构函数 destructor

    2 非平凡的类需要自己重新定义拷贝控制函数,赋值操作符 operator=

    3 复制控制 = 默认构造函数 default constructor + 拷贝构造函数 copy constructor + 析构函数 destructor + 赋值操作符 operator=

  • 相关阅读:
    设计模式9、组合模式 Composite
    char与varchar详解
    Presto
    灵活的IP网络测试工具——— X-Launch
    springboot 自动装配原理
    protobuf 中数据编码规则
    Vue的安装与配置
    docker-compose手册
    node---模块
    20_Vue如何监测数组类型数据发生改变的?
  • 原文地址:https://blog.csdn.net/ClamReason/article/details/126686592