拷贝构造函数是一种特殊的构造函数,用于在一个同类型对象的基础上生成一个新对象,他的函数形参列表有固定的写法(常量引用的形式)。
以二叉树节点的类Node为例:
class Node
{
public:
int index;
Node* left;
Node* right;
public:
Node(int i) //构造函数,带一个参数
{
index=i;
left=0;
right=0;
}
Node() //构造函数,不带参数
{
index=0;
left=0;
right=0;
}
Node(const Node& t) //拷贝构造函数的特点是参数表是一个(const className&),t是自定义的变量名
{
this->index=t.index;
left=0;
right=0;
}
}
拷贝构造函数以自己的一个同类对象为参数,生成一个新对象,比如:
Node t(10); //调用带一个参数的构造函数
Node x(t); //调用拷贝构造函数
//另外当我们使用赋值号生成新的对象时,系统也是调用拷贝构造函数
Node y=t; //等价于Node y(t);
总结就是:任何用自身同类对象生成新对象时,都会调用拷贝构造函数。
那么有一个问题,我刚刚说用赋值号的时候,系统会调用拷贝构造函数,那我没写拷贝构造函数,我难道不能用赋值号吗?
答:如果没写拷贝构造函数,系统免费送一个默认的。这个默认的构造函数就是简单的把各个属性都对应复制一遍。
这种默认的拷贝构造函数,大部分情况下都没有问题,除非:成员变量里有指针!!!
举个例子:
class Node
{
public:
int* number
public:
Node(const Node& t) //默认的拷贝构造函数就这么干的
{
this->number=t.number;
}
};
看看它干了啥!!!他把 t 的number里存的地址赋给了新对象的number,这意味着什么? 意味着新对象的number和t.number指着同一个空间,我在新对象里改了number所指空间的值,那么t.number指的空间值也就变了,这一般不是我们想要的,我们一般想要两个对象互不干扰,所以系统默认给的那种只是简单拷贝了指针地址的这种行为,我们叫浅拷贝,解决方法叫深拷贝,如下:
......
Node(const Node& t)
{
number=new int();
*number=*(t.number);
}
......
也就是手动开辟一个新的空间给number,然后把t的number值拷贝进去,这样他们才是互相独立了。
所以当我们的类里存在指针成员变量的时候,又有要用到拷贝构造函数的时候,就要自己动手实现深拷贝。