概念
拷贝构造函数是一种特殊的构造函数,其形参是本类对象的引用。其作用是使用一个已经存在的对象去初始化另一个同类的对象。
说明
1.在定义时通过等于号复制对象时,系统会自动调用拷贝构造函数。(注意,不是在任何时候等于号赋值都可以调用拷贝构造函数)
2.拷贝构造函数与原来构造函数实现了函数的重载。
(拷贝构造函数是构造函数的一个重载)
使用情景
- 一个对象作为函数参数,以值传递的方式传入函数体;
- 一个对象作为函数返回值,以值传递的方式从函数返回;
3)一个对象用于给另外一个对象进行初始化(常称为赋值初始化);
来自百度百科
特点
- 拷贝构造函数的本质还是构造函数,所以其函数名和类名相同也不能有返回值类型。
- 该函数只能有一个参数,并且是同类对象的引用。
- 每个类都必须由一个拷贝构造函数。程序员可以定义特定的拷贝构造函数,以实现同类对象间的传递。如果没有定义,系统会自动补齐一个缺省的拷贝构造函数。(每一个类都必须有构造函数、拷贝构造函数、一定要有析构函数。这三个函数就像是人体中细胞必须要有细胞体、细胞复制、细胞凋亡一样,是最基础的功能)
拷贝构造函数的定
class 类名
{
public:
类名(形参);
类名(类名&对象名);//拷贝构造函数
};
类名::类名(类名&对象名)
{}
//实例
class Human
{
public:
Human();
Human(Human&);
};
Human::Human(Human&){
cout<<"This human is copied!"<<endl;
}
拷贝构造函数的使用细节
下面这个例子说明多种情形下拷贝构造函数的使用,大家可以先计算一下这里调用了几次拷贝构造函数。
#include <iostream>
using namespace std;
#define Max(a,b)((a)>(b):(a):(b)cout<<a<<endl;)
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 marrage(Human hus,Human wife){
Human son(0);
return son;
}
Human Who_marraage_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 = marrage(pp,dd);
cout<<"--------"<<endl;
Who_marraage_money(dd,pp);
cout<<"--------"<<endl;
Who_control_everything(pp,dd);
cout<<"--------"<<endl;
return 0;
}
结果为:
-------
This human is copied!
--------
This human is copied!
--------
--------
This human is copied!
This human is copied!
--------
This human is copied!
This human is copied!
This human is copied!
--------
--------
接下来我们来分析一下为什么会这样。
int main() {
Human pp(1);//这里括号中值为1,只是调用了普通构造函数,符合函数重载的特点
cout<<"-------"<<endl;
Human son(pp);//这里括号内的值为一个同类对象,所以调用拷贝构造函数。
cout<<"--------"<<endl;
Human dd = pp; //这里“=”表示初始化操作,和()等价,所以也调用拷贝构造函数
cout<<"--------"<<endl;
son =pp;//这里虽然有等号,但是这里等号表示赋值,与上面的等号含义是不同的,属于浅拷贝不调用拷贝构造函数
cout<<"--------"<<endl;
son = marrage(pp,dd);//这里只有在pp和dd传输给形参时会调用拷贝构造函数,而return处并不会调用拷贝构造函数,这是个细节,函数体内定义的对象传回时是不调用拷贝构造函数的。
cout<<"--------"<<endl;
Who_marraage_money(dd,pp);//这里return处就调用了拷贝构造函数
cout<<"--------"<<endl;
Who_control_everything(pp,dd);//这里形参是类的引用,引用不分配内存,只是给变量取了个“绰号”而已,其本质没有出现初始化赋值操作,所以不存在拷贝构造函数的调用。
cout<<"--------"<<endl;
return 0;
}
浅拷贝和深拷贝
- 浅拷贝就是由缺省的构造函数所实现的数据成员逐一赋值,若类中含有指针类型的数据,则会产生错误。(如上例中
dd=pp;
的操作) - 因此,为了解决浅拷贝中出现的错误,必须显式地定义一个自己的拷贝构造函数,使之不但能拷贝数据成员,而且为对象1和对象2分配各自的空间,这就是所谓的深拷贝。
-为什么是浅拷贝?因为在拷贝构造函数中吸收的参数是该类的引用,系统是不给它分配对象的,所以无论怎么操作,最后生成的son或者dd都是和pp用同一个空间而已,并不会生成新的内存空间。(注意不是类的地址相同,而是数据成员用了同一个地址)
也就是说,在浅拷贝中,拷贝的变量和被拷贝的变量用的是同一块数据空间,当出现涉及内存的操作时,很容易出现问题。
深拷贝:通过在拷贝构造函数中定义新的内存来实现深入的拷贝效果。