C++中类的拷贝有两种:深拷贝,浅拷贝:当出现类的等号赋值时,即会调用拷贝函数
一、定义
如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。
浅拷贝:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅拷贝出来的对象也会相应的改变。
深拷贝:在计算机中开辟一块新的内存地址用于存放复制的对象。
二、区别
1 、在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的;但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象(野指针),所以,此时,必须采用深拷贝。
2、深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝,即必须自定义拷贝构造函数。
三、拷贝构造函数特点
1、 拷贝构造函数的本质还是构造函数,所以其函数名和类名相同也不能有返回值类型。
2、该函数只能有一个参数,并且是同类对象的引用。
3、每个类都必须有一个拷贝构造函数。程序员可以定义特定的拷贝构造函数,以实现同类对象间的传递。如果没有定义,系统会自动补齐一个缺省的拷贝构造函数。(每一个类都必须有构造函数、拷贝构造函数、一定要有析构函数。这三个函数就像是人体中细胞必须要有细胞体、细胞复制、细胞凋亡一样,是最基础的功能)
四、拷贝构造函数使用场景
1)一个对象作为函数参数,以值传递的方式传入函数体;
2)一个对象作为函数返回值,以值传递的方式从函数返回;
3 )一个对象用于给另外一个对象进行初始化(常称为赋值初始化);
五、实例
#include <iostream>
using namespace std;
class Human
{
public:
Human(int age_in):age(age_in){};
Human(Human&pp);
private:
int age;
};
Human::Human(Human&pp)
{
cout<<"This human is copied!"<<endl;
}
Human marriage(Human hus,Human wife)
{
Human son(0);
return son;
}
Human Who_manage_money(Human hus,Human wife)
{
return wife;
}
int Who_control_everything(Human& hus, Human&wife)
{
return 0;
}
int main() {
Human pp(1);
cout<<"-------"<<endl;
Human dd=pp;
cout<<"--------"<<endl;
Human son(dd);
cout<<"--------"<<endl;
son =pp;
cout<<"--------"<<endl;
son = marriage(pp,dd);
cout<<"--------"<<endl;
Who_manage_money(dd,pp);
cout<<"--------"<<endl;
Who_control_everything(pp,dd);
cout<<"--------"<<endl;
return 0;
}
结果:
解释:
int main() {
Human pp(1);//这里括号中值为1,只是调用了普通构造函数,符合函数重载的特点
cout << "-------" << endl;
Human dd = pp; //这里“=”表示初始化操作,和()等价,所以也调用拷贝构造函数
cout << "--------" << endl;
Human son(pp);//这里括号内的值为一个同类对象,所以调用拷贝构造函数。
cout << "--------" << endl;
son = pp;//这里虽然有等号,但是这里等号表示赋值,与上面的等号含义是不同的,
//属于浅拷贝不调用拷贝构造函数
cout << "--------" << endl;
son = marriage(pp, dd);//这里只有在pp和dd传输给形参时会调用拷贝构造函数,而return处并不会调用拷贝构造函数,
//这是个细节,函数体内定义的对象传回时是不调用拷贝构造函数的。
cout << "--------" << endl;
Who_manage_money(dd, pp);//这里return处就调用了拷贝构造函数
cout << "--------" << endl;
Who_control_everything(pp, dd);//这里形参是类的引用,引用不分配内存,只是给变量取了个“绰号”而已,
//其本质没有出现初始化赋值操作,所以不存在拷贝构造函数的调用。
cout << "--------" << endl;
system("pause");
return 0;
}