1.什么是拷贝构造
用一个存在的对象去初始化一个新对象时自动调用的一个函数
与一般构造函数的异同:
相同点:
1)都是在对象创建的时候自动调用
2)都有类自带的默认构造函数和拷贝构造函数,如果有显式的自己定义的构造函数和拷贝构造函数,则会覆盖默认的
3)都是用类名作为自己的函数名
4)构造函数和拷贝构造函数互为重载,看参数的传递
不同点:
1)构造函数是在对象创建的时候调用,但拷贝构造函数是新对象在创建的同时用已存在的构造函数来初始化这个对象
2)默认的构造函数是一个空函数,什么也不执行,但默认的拷贝构造函数执行了内容,赋值了每个非静态成员的值
2.拷贝构造函数的格式
类名(const 类名 &参数)
Cperson(const Cperson & a)
{
}
3.何时调用拷贝构造
1)类名 新对象(已存在的对象)
Cperson OldOb;
Cperson NewOb(OldOb);
2)类名 新对象 = 已存在的对象
Cperson OldOb;
Cperson NewOb = OldOb;
3)类名 新对象 = 类名(已存在的对象)
//右边的是一个临时对象,自己在VS2017中测试,一个单独的临时对象调用的是构造函数
Cperson OldOb;
Cperson NewOb = Cperson(OldOb);
4)类名 *新对象 = new 类名(已存在的对象)
Cperson OldOb;
Cperson *NewOb = new Cperson(OldOb);
注意:赋值不会调用拷贝构造:
Cperson OldOb;
Cperson NewOb;
NewOb = OldOb;
5)函数参数传递时
void fun(Cperson ob)
{
}
int main()
{
Cperson OldOb;
fun(OldOb);
system("pause");
return 0;
}
调用函数fun的时候,将实参对象传递给形参对象,这个过程就相当于是一个给新对象初始化的过程,所以会调用拷贝构造
6)函数返回一个对象时
Cperson fun()
{
Cperson a;
return a;
}
int main()
{
Cperson OldOb;
fun();
system("pause");
return 0;
}
返回值的是一个临时对象
4.拷贝构造的功能
逐个赋值每个非静态成员的值(成员的赋值成为浅赋值),默认的拷贝构造函数是浅拷贝
同一个类的不同对象,内存排布是一样的,但地址不同
5.深拷贝
浅拷贝存在的问题:
先看代码
#include <iostream>
using namespace std;
class Cperson
{
public:
int *a;
Cperson()
{
a = new int[2];
a[0] = 1;
a[1] = 2;
}
~Cperson()
{
delete[] a;
}
};
int main()
{
{
Cperson ob1;
Cperson ob2 = ob1;
}
system("pause");
return 0;
}
执行后却报错:
出现这种错误一般是数组越界、使用了野指针或者分母为零
那为什么会出现这种错误呢
首先创建对象ob1时,调用了构造函数,为对象ob1中的成员a开了一个数组空间并赋值,然后用对象ob1去初始化一个新对象ob2的时候,并没有调用构造函数,而调用的是拷贝构造,默认的拷贝构造只是将非静态成员的值简单赋值,所以将对象ob1中的指针成员a的值赋给了对象ob2中的指针成员a,由于a是一个指针变量,两个对象的a指向的是同一块内存地址;执行完后,对象ob1先执行析构函数,释放了对象ob1中指针a指针的内存,此时由于对象ob2中的指针a也是指向的这块内存,所以对象ob2中的指针a沦为了野指针,ob2执行析构函数的时候,尝试再次释放a指针的这块内存,所以出现错误
修改方法:自己写一个深拷贝
Cperson(const Cperson &ob)
{
this->a = new int[2];
memcpy(this->a, ob.a, 2 * 4);
}
总结:
1)对于有非静态指针成员的类,应该自己写深拷贝,为每个指针成员新开内存
2)尽量避免调用拷贝构造,对于函数参数的传递,可以采用引用,或者指针,不调用拷贝构造