拷贝构造函数与赋值操作符的具体实现
2008-09-20 23:56
274 查看
前面一篇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) 对于有基类的情况必须显式的调用基类的的赋值操作;
对于拷贝构造来说,不存在内存泄漏的问题,因为一般对象被创建的时候指针成员都还没有被分配内存,但是存在重复引用的问题。
所以为了避免这些问题,必须显式的定义这两个函数。
本文主要讨论这两个函数的实现。。
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) 对于有基类的情况必须显式的调用基类的的赋值操作;
相关文章推荐
- 拷贝构造函数与赋值操作符的具体实现 【转】参考度4.5星
- C++ 指针悬挂和赋值操作符的重载,拷贝构造函数实现
- C++ 指针悬挂和赋值操作符的重载,拷贝构造函数实现
- C++面试题,自己实现String类的构造函数,拷贝构造函数,赋值操作符,append,replace,+运算符重载等
- 关于为什么要为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
- 编写合适的构造函数、拷贝构造函数、赋值操作符、析构函数(后三者一般同时出现,称为“大三元法则”),那么这个概念就完整并且不用再考虑其资源(内存)管理的问题。
- 条款11: 为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
- 深入C++中构造函数、拷贝构造函数、赋值操作符、析构函数的调用过程总结
- 重点:包含动态分配成员的类 应提供拷贝构造函数,并重载"="赋值操作符。
- 实现赋值操作符要注意的问题
- 构造函数,拷贝构造函数,以及赋值操作调用的具体使用
- 类的构造函数 拷贝构造函数 赋值操作符 析构函数
- 为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
- 为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
- 深入C++中构造函数、拷贝构造函数、赋值操作符、析构函数的调用过程总结
- 子类的拷贝构造函数和赋值操作符
- 拷贝构造函数和赋值操作符
- C++中的拷贝构造函数和赋值操作符
- 构造函数,拷贝构造函数,赋值操作符的调用时机
- C++类中的4个特殊函数 - 缺省构造函数、拷贝构造函数、拷贝赋值操作符和析构函数