参考文章 :
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对象超出作用域,接下来调用析构函数
2(默认)浅拷贝的问题
像上面的m_age这种成员变量,在对象之间的赋值(浅拷贝)的时候,就会导致两个对象的指针成员都指向了同一个地址。
Student stu1;
Student stu2;//(1)
stu2 = stu1;//(2)出问题了!!!两个指针成员同时指向了一个堆变量
因为一个对象要析构的时候会释放这个地址的内存。另一个对象释放的时候也要释放这个内存。
这会导致两次释放同一个内存,第一次释放内存的时候内存就归还给系统了。
当你第二次再释放这个内存的时候,你在释放不再归你管的内存,操作系统会把你的程序杀掉。运行的时候你的程序就会闪退。
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)
(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;
}
4 复制控制
复制控制,专门指上文提到的深拷贝。如需要专门重写拷贝构造函数,赋值操作符来加以管理。
1 C++提供三个默认实现:默认构造函数 default constructor;拷贝构造函数 copy constructor; 析构函数 destructor
2 非平凡的类需要自己重新定义拷贝控制函数,赋值操作符 operator=
3 复制控制 = 默认构造函数 default constructor + 拷贝构造函数 copy constructor + 析构函数 destructor + 赋值操作符 operator=