//**********************
//** ch18_7.cpp **
//**********************
#include <string.h>
#include <iostream.h>
class Name{
public:
Name(){ pName = 0; }
Name(char* pn){ copyName(pn); }
Name(Name & s){ copyName(s.pName); }
~Name(){ deleteName(); }
Name & operator =(Name & s) //赋值运算符
{
deleteName();
copyName(s.pName);
return *this;
}
void display(){ cout << pName << endl; }
protected:
void copyName(char* pN);
void deleteName();
char* pName;
};
void Name::copyName(char* pN)
{
pName = new char[strlen(pN) + 1];
if(pName)
strcpy(pName, pN);
}
void Name::deleteName()
{
if(pName){
delete pName;
pName = 0;
}
}
void main()
{
Name s("claudette");
Name t("temporary");
t.display();
t = s; //赋值
t.display();
}
在构造函数中该存储区是从堆中分配来的,存在浅拷贝问题,必须自定义赋值运算符和拷贝构造函数。
赋值运算符以operator=()的名称出现,看起来像一个析构函数后面跟着拷贝构造函数。
就是这个代码:
Name & operator =(Name & s) //赋值运算符
{
deleteName();
copyName(s.pName);
return *this;
}
void copyName(char* pN)
{
pName = new char[strlen(pN) + 1];
if(pName)
strcpy(pName, pN);
}
通常赋值运算符有两部分,第一部分与析构函数类似,在其中取消对象已经占用的资源。第二部分与拷贝函数类似,在其中分配新的资源。(就是上面的代码。)
其实上面就是深拷贝的情况了。堆分配的资源最后得delete释放。而那样会引起二义性,会第二次释放已经释放的资源,已经释放的资源的指针早就已经成为野指针了,再对那块未知的位置释放空间会造成错误甚至程序崩溃。
如图就是:

Delete无论谁先释放其空间 对方都会释放一个无法找到的空间可能。因为原来的那个空间的地址早就不存在了。
那么上面代码怎么解决的呢?
解决办法就是在赋值的时候处理下。过程如下:
创建两个对象,A,B。然后为A存放写资源,在把A赋值给B的时候由赋值运算符函数来处理了。
看图:
本来是:
Name A("claudette");
Name B("temporary");
然后:

上面图的演示正是代码的实现的效果。A的资源和B的资源各自一份。Delete释放的时候也不会造成因无法释放未知空间而崩溃了。
上面的讨论到此为止。
PS:
那么有人可能再想,那么麻烦上面操作,我狠一点,既然释放会出问题那么我不用delete释放堆空间不就行了。他是根据这句话来的:“堆对象的作用域是整个程序生命期,所以除非程序运行完毕,否则堆对象作用域不会到期。”
确实这样的确可以防止那种情况,但是代价是内存的占用比较大了,一旦程序大了一直开辟不释放可能会内存溢出。故使用上面自定义复制运算符和自定义拷贝构造函数是很有用的。
参考文献:《C++程序设计教程--钱能》
本文详细介绍了C++类中如何实现深拷贝及赋值运算符,避免了浅拷贝导致的资源重复释放问题,通过实例展示了深拷贝的正确实现方式,并解释了其原理及注意事项。
4812

被折叠的 条评论
为什么被折叠?



