一、什么是拷贝构造函数?
构造函数按类型可以分为 普通构造函数 与 拷贝构造函数
拷贝构造函数格式:类名(const 类名& 形参名)
默认拷贝构造函数作用:对属性进行值拷贝(遇到指针会出现深拷贝与浅拷贝的问题),代码类似于
Person(const Person& p) {
this->m_a = p.m_a;
this->m_b = p.m_b;//会出现浅拷贝带来堆区内存重复释放问题
}
int m_a;
int *m_b;
编译器会给类自动添加至少3个函数(还有个赋值运算符operator=)
①无参构造函数
②析构函数
③拷贝构造函数
注意:当自定义拷贝构造函数的时候,系统不会自动添加其他函数(应该包括析构函数)
当自己定义有参时,系统不会添加无参,但是会有拷贝构造函数
二、什么时候调用拷贝构造函数
拷贝构造函数在三种情况下调用
1、用已经创建好的对象为新对象初始化
Person p1(10);
//以下两种都会调用拷贝构造函数
Person p2(p1);
Person p3 = p1;//不是赋值,是初始化
那么对象何时销毁呢?
对象何时销毁也与其作用域有关。
变量(对象是类类型的变量)的作用域决定了变量的有效范围,变量的有效作用域从它的定义点开始,到这个变量上面最邻近的开括号与之配对的闭括号。
2、以值的方式传递给函数
void doWork(Person p) {
}
void test02() {
Person p1(10);
doWork(p1);
}
3、函数以值的方式返回
Person doWork2() {
Person p(10);
return p;
}
void test03() {
doWork2();
}
三、深拷贝与浅拷贝
浅拷贝:对值进行赋值,编译器写的是浅拷贝
深拷贝:创建一个新的堆区,进行拷贝
例如:
当使用系统所给的拷贝构造函数时(浅拷贝)
当时由于堆区开辟的内存,需要自己手动释放,所以添加析构函数
class Person
{
public:
int* m_A;
Person(int a) {
m_A = new int(a);//注意new开辟的堆区内存返回的是指针
}
~Person() {
if (m_A!=NULL)
{
delete m_A;
m_A = NULL;
}
}
};
此时运行系统错误,为什么呢?
因为浅拷贝将 p1的m _a的值,也就是地址,直接赋给了p2的m_a,当由于栈区是先进后出,所以p2先调用析构函数,释放了堆区,由于p1与p2的m_a指向的地址相同,所以p1再释放的时候出现重复释放堆区内存。
所以需要通过深拷贝,新开辟一个堆区,进行拷贝
Person(const Person& p) {
this->m_A = new int(*p.m_A);
}
浅拷贝与深拷贝问题主要是出现在类属性有在堆区开辟的情况下。