[转] 一个帖子,很能说明这个问题
问题:
以下这段代码执行后会不会有问题?
基类:
class CBase
{
public:
virtual void VirtualFun1(CString strFun1) = 0;
};
子类:
class CDerived : public CBase
{
public:
CDerived();
virtual ~CDerived();
virtual void VirtualFun1(CString strFun1);
private:
CString m_strFun1;
};
CDerived::CDerived()
{
}
CDerived::~CDerived()
{
}
void CDerived::VirtualFun1(CString strFun1)
{
m_strFun1 = strFun1;
}
客户代码:
CBase *pBase = new CDerived();
pBase->VirtualFun1("test");
delete pBase;
答案/分析:
会!
只要一运行,就会发生内存泄漏(我的测试平台:VC6);
调试器输出信息:
Dumping objects ->
strcore.cpp(118) : {76} normal block at 0x003A36E8, 16 bytes long.
Data: < tsr > 01 00 00 00 03 00 00 00 03 00 00 00 74 73 72 00
Object dump complete.
注:
01 00 00 00 03 00 00 00 03 00 00 00 74 73 72 00
这部分数据可能是调试器的内存保护数据,未验证
问题表面是在于m_strFun1,问题的实质其实是CBase 没有提供虚拟析构函数,只要为CBase 加入一个虚拟析构函数即解决了上面的问题;
原因就在于当delete pBase的时候,只是调用了CBase的析构函数(非虚拟,编译器背地里完成,对于纯虚拟类还没有验证过),从而导致未调用CDerived的析构函数,进而造成m_strFun1没有被释放内存。
事后,查了一下资料,其实在《Effective C++》中的条款14中对此进行了详细的解释,有兴趣的可以去参考一下。
如果单单分析这段代码,其实还是好解决的,如果把这段代码放到一个大程序中,出现类似的内存泄漏时,可能就很难追查了,所以还是要打好基础,写好每一行代码。
总结:
1. C++基本功不扎实,对析构函数了解不够,对编译器的内部行为不够了解;
2. 忽视了代码的变化,特别是向导生成的代码,一般通过向导会自动建立一个虚拟析构函数;
3. 发现问题的关键比解决问题更难;由于这个内存泄漏是发生在一个比较大的程序中,并且还涉及到其它dll,花了很长时间对它进行定位,确定发生泄漏的代码行;
4. 调试是对基本知识的综合考验,最能反映一个人的技术深度,深入剥析后就能加深技术细节的理解和记忆;