条款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得到的对象
{
vector<Widget*> vwp;
... // 同上
for (vector<Widget*>::iterator i =vwp.begin(); i != vwp.end(), ++i)
{
delete *i;
}
}
但如果在用指针填充了vwp和你要删除它们之间抛出了一个异常,你会再次资源泄漏。
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>()); // 虚析构函数的基类
} // 指针来删除派生对象
把模板化从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之前抛出一个异常,就会发生泄漏。最简单的可能是用智能指针的容器来代替指针的容器,典型的是引用计数指针。
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泄漏,甚至
// 在上面代码中抛出异常
当一个指针的容器被销毁时,会销毁它(那个容器)包含的每个元素,但指针的“析构函数”是无操作!它肯定不会调用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泄漏,甚至
// 在上面代码中抛出异常
相关文章推荐
- 条款16:成对使用new和delete时要采取相同形式
- Effective STL 第7条:如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉
- 条款16:成对使用new以及delete的时候应该采取相同的形式
- Effective C++:条款16:成对使用new和delete时要采取相同形式
- Effective C++ 条款15、16 在资源管理类中提供对原始资源的访问||成对使用new 与 delete要采取相同形式
- 读书笔记《Effective c++》 条款16 成对使用new和delete时要采用相同形式
- effective C++ 条款 16:成对使用new和delete时要采用相同形式
- 《Effective C++》学习笔记条款16 成对使用new和delete时要采取相同形式
- 条款3,4:尽量用new和delete而不用malloc和free 尽量使用c++风格的注释
- 条款16:成对使用new和delete时,采取相同的形式
- Effective C++ 条款16 成对使用new和delete时要采取相同形式
- 条款16:成对使用new和delete时要采取相同的形式
- 读书笔记《Effective C++》条款16:成对使用new和delete时要采取相同形式
- 条款16:成对使用new和delete时要采取相同的形式
- 条款16:成对使用new和delete时要采取相同形式
- 容器内指针的new和delete
- 条款16 成对使用new和delete时要采用相同的形式
- 条款33:提防在指针的容器上使用类似remove的算法
- 条款5:使用相同的形式的new和delete
- 读书笔记_Effective_C++_条款十六:成对使用new和delete时要采取相同的形式