上一期中我们讲述了构造函数的相关内容,谈到构造函数在形式上有几种分类,即带参数的、不带参数的以及参数列表初始化的,还有一种传引用的构造函数,称为拷贝构造函数,顾名思义,就是起到拷贝的功能,通过一个已经存在的类对象的相关数据初始化一个新的类对象。
我们还是用Point这个类作为例子进行讲解,拷贝构造函数就是参数类型为引用类型的构造函数。
class Point
{
double x,y;
public:
Point(Point & point);
};
所有的类都有自己的拷贝构造函数,如果程序员自己没有写拷贝构造函数,那么系统会默认生成一个缺省的拷贝构造函数,它采取逐位复制的方法进行对象拷贝,又称为浅拷贝。
我们自己写一个Point类的浅拷贝的拷贝构造函数作为例子:
Point::Point(Point & point)
{
x=point.x;
y=point.y;
}
这就是浅拷贝,即逐位复制。看样子没什么毛病,为什么说它浅呢?
实际上,如果数据成员都是基本数据类型,浅拷贝是没有什么问题的。
但是,当一个类的数据成员中含有指针的时候,浅拷贝就会出事。
我们把Point类修改一下,增加一个指针指向Point类,为了安全,我们把这个指针初始化为NULL:
class Point
{
double x,y;
Point * p=NULL;
public:
Point(Point & point);
};
然后我们用浅拷贝:
Point::Point(Point & point)
{
x=point.x;
y=point.y;
p=point.p;
}
首先x和y是没有问题的,指针p的值虽然是相同了,但是我们的目的是想让指针的值相同吗,显然不是,是想让它们指向的内存单元具有相同的值,如果采用这样的浅拷贝,那么这两个指针指向的是同一块内存,有同学可能会说,这好像没有问题啊,这不也成功复制了吗?问题在于一旦我们修改其中一个类对象的p指向的内容,那么另一个类对象的数据也会改变,因为它们指向的是同一块内存。因此,我们在这种情况下需要用深拷贝。
通过上面的分析,我们知道浅拷贝的问题在于,这两个指针指向了同一块内存,所以只要给生成的类对象一块新内存空间就可以解决问题了:
Point::Point(Point & point)
{
x=point.x;
y=point.y;
if(point.p)
{
p=new Point;
p->x=point.p->x;
p->y=point.p->y;
}
}
在这里你可以看到,我们是首先判断p是不是空指针,不是空指针我们再复制,这里非常重要,如果没有这个判断,一旦p是空指针,而我们还是继续操作,引发系统不可预知的错误。