Chinaunix首页 | 论坛 | 博客
  • 博客访问: 548289
  • 博文数量: 493
  • 博客积分: 2891
  • 博客等级: 少校
  • 技术积分: 4960
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 17:11
文章分类

全部博文(493)

文章存档

2010年(493)

分类:

2010-05-12 18:23:59

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,所以在父类中释放一个子对象时,需格外小心。
阅读(446) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~