小满的C++学习心得(3) 小心析构函数和复制构造函数!

还没两天呢又到这里来写BLOG了,没办法,《C++ Primer Plus》的第12章确实很不好学。关于类和动态内存分配的内容,很多东西你好像是看懂,但等到你真正自己动手编程时才发现自己根本没有真正理解。这不,我在做书后第3道编程练习题时又碰了钉子。这道题要求把前几章的一个例子程序改造一下,把私有类成员里原来固定长度的自动变量char[]字符串改成char*,等到使用时再用new分配合适的内存空间。看似很简单,我刷刷刷地写好了,以下是头文件中的类声明:

这个声明和原来相比改动不大,仅仅是把字符串company的类型从原来定长的char[50]改成了char*,外加多了一个析构函数。不过,因为涉及到分配动态内存,因此两个构造函数的定义有较大的改动。以下是两个构造函数外加析构函数的定义:(其它与本文无关的方法函数在此省略)

OK,到此为止好像一切都很完美。加上原来给的主程序,感觉应该成功了:

此时悲剧出现了,在执行完系统暂停system("pause");按任意键恢复运行后,屏幕上跳出了一个刺眼的错误消息框:

编程序的人都晓得,程序不怕编译错误,就怕那些谁也说不清的bug,而且这种bug还是系统崩溃的bug,未提供可供直接参考的错误信息,让人头疼,并且无从下手。不过它是在系统暂停恢复后出现的,主程序里系统暂停语句后就是return语句了,也就是说,很可能是析构函数出了问题。为了验证这个假设,我在析构函数处设置了断点。果不其然,程序走到析构函数时中断后,按F11执行单步跟踪程序居然不往下走,按两下后那个错误就跳了出来。

问题好像是出在析构函数,可是析构函数总共就一句话,不那么写你还能怎么写呢?我感觉到可能问题还出在更深层的地方,于是上CSDN里寻求帮助,没找到问题的直接答案,但是找到了一些很有帮助的信息——有些高手说出现这种崩溃错误的原因很可能是delete试图去释放一个已经不饿释放了的指针!

同一个指针被释放两次?难道说析构函数调用的次数比构造函数还要多?我想看看内存中类存储的情况,于是把断点往前设了几个语句。通过在中断时检查变量监视器可以很清楚的了解到内存利用的情况:

有没有发现bug?其中有两个对象的不但内容一样,company成员还指向了同一个地址!(见红框)也就是说,这些对象在逐个析构时company的那个地址真的会被delete两次啊!有木有!

top这个对象哪里来的?我当时一时想不起来,原来它在主程序中间的位置才出现的,那是一个求最大值对象的循环,top用于记录值最大的那个对象,而且原题中没有说要对这个功能模块进行修改,我也就依葫芦画瓢想也没想就抄了上去。现在再来仔细看一遍这段程序:

它第1句使用了等号进行了对象的初始化,第3句也用了等号进行了对象的复制。然而,斯蒂芬老师在书中提到,这些复制都是所谓的浅复制 ——对于company成员,它只是简单地把内存地址赋给新的对象的company成员,自己却未去开辟空间存储它。也就是说,两个对象中的company指针指向了同一段内存,那么析构时出问题也便是必然的了。

怎么解决呢?书里说的很清楚,对于第一种在初始化时的复制使用复制构造函数,第二种普通的赋值要用重载=号的方法。两者所要完成的任务差不多,都是显式地要告诉C++不要仅仅复制指针,而要连内容一起也复制进来,这就是所谓的深复制 。下面是复制构造函数和“=”号重载的定义:

这样一来,问题就解决了。从变量将监视器中看出,两个Stock对象的company成员使用了不同的地址:(见红框)

至此,我不得不佩服斯蒂芬老师的过人之处,这道编程题表面上是考如何使用动态内存,实际上醉翁之意不在酒,题目的精华实际上完全在那个题中好像只字未提的“=”号上。通过这个题,我对于复制构造函数有了深刻而鲜活的认知。高,实在是高!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值