导致delete调用容易遗漏的原因
在C++的开发中,在堆内存中创建和销毁对象需要调用关键字new和delete来完成,new和delete的调用总是成对出现的,否则会出现内存泄漏的情况。
一般情况下,new和delete调用会在同一个函数内部完成,比如下面的代码:
#include <iostream>
class TestCls
{
public:
TestCls () {}
void show() {
std::cout << "I am TestCls" << std::endl;
}
};
int main(int argc, char *argv[])
{
TestCls *pTestCls = new TestCls;
pTestCls->show();
delete pTestCls;
return 0;
}
这种情况下,很容易保证由new创建的方法被正确的delete掉。然而,当创建的对象作为函数的返回值供给其他函数使用时,delete的方法调用很容易忽略掉,比如下面的代码:
#include <iostream>
class TestCls
{
public:
TestCls(int id) : m_id(id) {}
void show() {
std::cout << "I am TestCls " << m_id << std::endl;
}
private:
int m_id;
};
TestCls *buildTestCls(int id)
{
return new TestCls(id);
}
int main(int argc, char *argv[])
{
TestCls *pTestCls = buildTestCls(1);
pTestCls->show();
delete pTestCls;
pTestCls = buildTestCls(2);
pTestCls->show();
delete pTestCls;
return 0;
}
因delete和new不在同一个域内,这很容易导致delete调用遗漏,同时又由于函数可以被多次调用,导致需要代码中出现delete的次数与new的次数不一致,使得代码检查变得非常困难。
防止遗漏的方法
因为堆栈中申请的对象析构函数是在变量作用域结束时自动调用的,我们可以借用类的析构函数把需要释放的对象在析构函数中进行释放。
为此,我们可以实现以下模板,用来存放对象指针,进行自动释放,模板实现方法如下:
template<class Data>
class any_ptr
{
protected:
Data *m_pData;
public:
any_ptr(Data *pData) : m_pData(pData) {}
virtual ~any_ptr()
{
delete m_pData;
}
Data * operator->()
{
return m_pData;
}
Data & operator*()
{
return *m_pData;
}
Data * Value()
{
return m_pData;
}
private:
any_ptr(const any_ptr&);
any_ptr& operator =(const any_ptr&);
};
其中操作符operator*和operator->使得对该模板的使用方法与它包裹的原始类的使用方法在形式上完全相同。
使用该类后,上面例子中的main函数,不需要调用delete方法,内存也不会泄露,改变后的代码如下:
int main(int argc, char *argv[])
{
any_ptr<TestCls> pTestCls1(buildTestCls(1));
pTestCls1->show();
//delete pTestCls1;
any_ptr<TestCls> pTestCls2(buildTestCls(2));
pTestCls2->show();
//delete pTestCls2;
return 0;
}