今天花了好长时间在项目中找到一个内存泄漏的bug,其根本原因是设计思想在作怪。犯了C++之兵家大忌,在析构中调用
了虚函数。拿出来和大家分享一下~_~
假设我们有这样的一个设计:
class ItemContainBase //存放元素的容器
{
public:
ItemContainBase();
virtual ~ItemContainBase();
void Destory(void); //销毁(主要是调用DestoryItems)
protected:
list<Item*> m_items;
private:
virtual void DestoryItems(); //销毁容器的元素(如果子类有自已的元素要销毁, 就要重写这个函数)
}
ItemContainBase::ItemContainBase()
{
}
ItemContainBase::~ItemContainBase() //这里希望通过多态实现所有子类通用的销毁操作
{
Destory();
}
void ItemContainBase::Destory(void)
{
DestoryItems();
//do other thing here....
}
void ItemContainBase::DestoryItems(void)
{
//delete m_items's item
for (list<Item*>::iterator iter=m_items.begin(); iter!=m_items.end(); ++iter)
{
if(*iter != NULL)
{
delete *iter;
}
}
}
class ItemWindowContain : public ItemContainBase //存放元素的窗口容器
{
public:
ItemWindowContain(){}
~ItemWindowContain(){} //在父类中已经用多态实现了销毁的操作,所有这里为空
private:
virtual void DestoryItems(); //重写父类的
list<Window*> m_itemWindos; //主要是为元素动态生成窗口
};
void ItemWindowContain::DestoryItems()
{
ItemContainBase::DestoryItems();
//delete m_itemWindos's item
for (list<Window*>::iterator iter=m_itemWindos.begin(); iter!=m_itemWindos.end(); ++iter)
{
if(*iter != NULL)
{
delete *iter;
}
}
}
测试代码如下:
ItemWindowContain* pWindowContain = new ItemWindowContain;
delete pWindowContain;
在析构的时候,结果会按照设计者的思想去销毁ItemWindowContain中的m_itemWindos和继承下来的
m_items中的所有元素吗? 答案是否定的。在ItemContainBase的析构函数中并没有对DestoryItems
进行多态的操作,它调用了自己的DestoryItems也就是ItemContainBase::DestoryItems. 这样悲剧就
发生了,如果不停的使用new delete ItemWindowContain元素; 就会不停地内存泄漏. 同样的情况也会
发生在构造函数中。
如果你在开发过程中碰到了r6025 pure virtual function call的错误,那么你很可能就是用了上面的
错误的设计思想. 在构造或析构函数中间接地调用了纯虚函数。
所以得出一个结论: 绝不能在构造或析构函数中间接调用纯虚函数,也不要调用虚函数。