前面一篇blog提到过对含有指针成员变量的类来说,使用默认的拷贝构造和赋值操作是不安全的,具体的原因是默认的函数都属于浅拷贝,所谓浅拷贝就是指只对指针本身进行拷贝操作而对于指针所指向的内容不进行任何操作,这显然至少会带来2个问题,第一个是内存泄漏,因为指针本身被一个新值所覆盖,造成指针原先指向的内存将无法得到释放的机会。另一个问题就是重复引用问题,两个(或者更多)的指针指向同一块内存,当对其中一个指针进行delete操作后,所有对其他指向这块已经被释放的内存的指针的操作,结果都是无法预测的。同样,当修改其中一个对象的指针成员所指向内存中的数据时,其它对象会跟着一起被改变,这显然是错误的。
对于拷贝构造来说,不存在内存泄漏的问题,因为一般对象被创建的时候指针成员都还没有被分配内存,但是存在重复引用的问题。
所以为了避免这些问题,必须显式的定义这两个函数。
本文主要讨论这两个函数的实现。。
1. 拷贝构造函数
假定有以下类型
struct CTREE_NODE_TAG
{
TYPE data;
int parent;
CList<int, int&> child;
}
由于存在CList类型成员child, 所以须实现拷贝构造以及赋值操作符函数;
CTREE_NODE_TAG(const CTREE_NODE_TAG& rhs)
:data(rhs.data), parent(rhs.parent)
{
// child.RemoveAll();
int* pNode;
POSITION positionRhs = rhs.child.GetHeadPosition();
POSITION positionTarget = child.GetHeadPosition();
while(positionRhs!=NULL)
{
if(positionTarget == NULL)//只有当目标对象长度小于拷贝对象时才创建新的结点;注:这里可以不需要,应为初始化时child的长度为0
{
pNode = new(int);
*pNode = rhs.child.GetNext(positionRhs);
child.AddTail(*pNode);
}
else//
{
*pNode = rhs.child.GetNext(positionRhs);
child.SetAt(positionTarget, *pNode);
child.GetNext(positionTarget);
}
}
while(positionTarget != NULL)//Free the extra element 注:这里可以不需要,应为初始化时child的长度为0
{
child.RemoveAt(positionTarget);
child.GetNext(positionTarget);
}
}
对于非指针类型跟默认实现一样直接复制就可以了,对于指针类型的成员变量,则需要对指针所指向的内容进行复制,而不是简单的对指针本身进行复制;
注意: 当增加了新的成员变量时,不要忘了更新拷贝构造函数;
2. 赋值操作符
对于赋值操作符基本上跟拷贝构造类似,但需要注意以下几点:
1) 防止内存泄漏: 对于指针型成员变量(动态分配内存的)要先释放内存;
2) 对于有基类的情况必须显式的调用基类的的赋值操作;