1 现象:问题描述
在一次版本测试过程中发现:话单合并功能有内存泄漏,待合并话单的key值对象没有释放。将key值对象释放后,程序异常退出。
2 关键过程:根本原因分析
经代码走读,发现如下代码引起程序异常退出:
class CKey
{
public:
//实际完成计算的对象指针。
CKey* m_pDelegation;
public:
CKey();
/*
Copy构造函数,当CKey对象被以Copy的方式传值时,
传递m_pDelegation指针。
*/
CKey(CKey& key);
virtual ~CKey();
….
CKey::CKey()
{
m_pDelegation = NULL;
}
//初始化:赋值拷贝的方式
CKey::CKey(CKey& key)
{
m_pDelegation = key.m_pDelegation;
key.m_pDelegation = NULL;
}
//析构函数
CKey::~CKey()
{
if (NULL != m_pDelegation)
{
delete m_pDelegation;
m_pDelegation = NULL;
}
}
class CBillKey : public CKey
{
public :
VECTOR m_KeyValue;
public :
CBillKey();
CBillKey(VECTOR& KeyValue);
~CBillKey();
...
}
//构造函数,初始化索引
CBillKey::CBillKey(VECTOR& KeyValue)
{
VECTOR::iterator iter = KeyValue.begin();
TVariant tvTmp;
while (iter != KeyValue.end())
{
tvTmp = *(*iter);
m_KeyValue.push_back(tvTmp);
iter ++;
}
m_pDelegation = this;
}
注意以上代码的红色部分,由于CBillKey继承CKey,且在CBillKey对象中将this指针传递给CKey中的m_pDelegation,在释放CBillKey对象时,首先调用CKey类的析构函数,而此时CKey析构函数中的m_pDelegation指向了CBillKey对象,于是继续调用CBillKey的析构函数,导致程序进入析构函数的递规调用死循环,直到栈空间耗尽后程序异常退出。
3 结论:解决方案及效果
第一种解决方案:
在释放CBillKey对象之前判断m_pDelegation对象是否指向自身,这样在析构父对象的过程中,就不会再调用子对象的析构函数了。
CKey::~CKey()
{
if (this != m_pDelegation)
{
delete m_pDelegation;
m_pDelegation = NULL;
}
}
修改后,程序恢复正常。
第二种解决方案:
在由于程序中使用CHashTable来保存待合并话单的关键字段,在HashTable中保留一个CKey对象,而通过m_pDelegation进行具体的CKey子类对象的操作。可以直接定义Hashtable为CHashTable,在HashTable中保留CKey对象的指针。这样就可以去掉m_pDelegation。
4 经验总结:预防措施和规范建议
注意构造函数和析构函数的调用顺序,在构建一个子对象时,首先调用父类的构造函数,接着调用自身的构造函数;而析构的时候正好相反,首先调用自身的析构函数,接着调用父类的析构函数。
由于绝大多数的类的析构函数会定义为virtual,所以在父类中释放一个子对象时,需格外小心。
阅读(480) | 评论(0) | 转发(0) |