您的位置:首页 > 其它

条款7:当使用new得指针的容器时,记得在销毁容器前delete那些指针

2014-04-01 17:05 267 查看
如果容器容纳的是指向通过new分配的对象的指针时:

当一个指针的容器被销毁时,会销毁它(那个容器)包含的每个元素,但指针的“析构函数”是无操作!它肯定不会调用delete。

例子:

下面代码直接导致一个内存泄漏:

void doSomething()

{

vector<Widget*> vwp;

for (int i = 0; i < SOME_MAGIC_NUMBER;++i)

vwp.push_back(new Widget);

... // 使用vwp

} // Widgets在这里泄漏!

问题:vwp的每个元素都被销毁,但那并不改变从没有把delete作用于new得到的对象

解决1,有缺点:

void doSomething()

{

vector<Widget*> vwp;

... // 同上

for (vector<Widget*>::iterator i =vwp.begin(); i != vwp.end(), ++i)

{

delete *i;

}

}

但如果在用指针填充了vwp和你要删除它们之间抛出了一个异常,你会再次资源泄漏。

好的解决2:需要把delete转入一个函数对象

template<typename T>

struct DeleteObject : //

public unary_function<const T*, void>{ // 这里有这个继承

void operator()(const T* ptr) const

{

delete ptr;

}

};

void doSomething()

{

... // 同上

for_each(vwp.begin(), vwp.end(),DeleteObject<Widget>);

}

问题:需要你指定了DeleteObject将会删除的对象的类型(在本例中是Widget);

  而且如果vwp是一个vector<Widget*>,所以当然DeleteObject会删除Widget*指针!

但会产生这种冗余,导致很难跟踪到的bug:比如,有的人恶意地故意从string继承:

class SpecialString: public string { ... };

因为string,就像所有的标准STL容器,缺少虚析构函数,而从没有虚析构函数的类公有继承是一个大的C++禁忌。

例子:

void doSomething()

{

deque<SpecialString*> dssp;

...

for_each(dssp.begin(), dssp.end(), //
行为未定义!通过没有


DeleteObject<string>()); // 虚析构函数的基类

} // 指针来删除派生对象

解决3:

可以通过编译器推断传给DeleteObject::operator()的指针的类型来消除这个错误

把模板化从DeleteObject移到它的operator():编译器知道传给DeleteObject::operator()的指针的类型,可以让它通过指针的类型自动实例化一个operator()。

struct DeleteObject { // 删除这里的

// 模板化和基类

template<typename T> // 模板化加在这里

void operator()(const T* ptr) const

{

delete ptr;

}

}

新版DeleteObject:

void doSomething()

{

deque<SpecialString*> dssp;

...

for_each(dssp.begin(), dssp.end(),DeleteObject());// 啊!良好定义的行为!

}

问题:但仍不是异常安全的。如果在SpecialString被new但在调用for_each之前抛出一个异常,就会发生泄漏。最简单的可能是用智能指针的容器来代替指针的容器,典型的是引用计数指针。

解决4:引用型智能指针

利用Boost的shared_ptr:

void doSomething()

{

typedef boost::shared_ ptr<Widget> SPW; //SPW ="shared_ptr to Widget"

vector<SPW> vwp;

for (int i = 0; i < SOME_MAGIC_NUMBER;++i)

vwp.push_back(SPW(new Widget)); // 从一个Widget建立SPW,

// 然后进行一次push_back

... // 使用vwp

} // 这里没有Widget泄漏,甚至

// 在上面代码中抛出异常
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐