复制控制 copy control(非平凡的类)(深拷贝)

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

参考文章 :

自定义类型 struct/class

对象之间赋值

1 非平凡的类

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

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

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

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

#include<iostream>
using namespace std;

class Student
{
public:
    int* m_age;//指针成员:管理动态内存
    //构造函数中申请堆内存
    Student() :m_age(new int(18)) //3 执行构造函数
    {
    }
    ~Student()//5 析构函数释放堆内存
    {
        delete m_age;
    }
};

int main()
{
    //1 main函数开始执行
    Student stu;//2 在栈上创建新对象stu(分配内存),接下来调用构造函数

    return 0;
}//4 stu对象超出作用域,接下来调用析构函数

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

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

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

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

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

 

2(默认)浅拷贝的问题

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

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

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

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

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

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

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

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

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

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

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

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

3.1 拷贝构造函数copy constructor

非平凡类的拷贝构造函数

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

class Student
{
public:
    int* m_age;//指针成员:管理动态内存
    //构造函数中申请堆内存
    Student() :m_age(new int(18)) 
    {
    }
    //copy constructor 拷贝构造函数(函数名为类名,参数为同类型的另一个对象
    Student(const Student& stuFrom);
    ~Student()
    {
        delete m_age;//析构函数释放堆内存
    }
};

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

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

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

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

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

(1)创建stu1对象的时候

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

(2)函数传值

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

(3)函数返回值类型

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

3.2 赋值操作符

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

Student stu1;
*stu1.m_age = 18;
Student stu2;
*stu2.m_age = 19;
stu2 = stu1;//赋值操作符

class Student{
  Student& operator=(const Student& stuFrom);
};
// stu2 
Student& Student::operator=(const Student& stuFrom){
  if(this == &stuFrom)//自己赋值给自己,直接返回自己
  {
     return *this;
  }
  *m_age = *stuFrom.m_age;//深拷贝,将存储的数据拷贝,而不是直接地址拷贝
  return *this;
}

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

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

4 复制控制

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

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

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

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

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C++程序员Carea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值