7.当使用new的指针容器时,记得在销毁容器前delete那些指针
当容器容纳的是指向通过new分配的对象的指针时,当指针容器被销毁时,不会销毁里面的指针,故会造成内存泄漏
class Widget
{
public:
private:
};
void doSomething()
{
vector<Widget *> vwp;
for (int i = 0; i < size; ++i)
{
vwp.push_back(new Widget)
}
}//如果直接退出会引起资源泄露
//解决方法1,函数中添加循环。
//缺点:这段代码不是异常安全的,如果在向vwp中填充指针和从中删除指针
//的过程中有异常抛出的话,同样有资源泄露
void doSomething()
{
vector<Widget *> vwp;
for (int i = 0; i < size; ++i)
{
vwp.push_back(new Widget);
}
for (vector<Widget*>::iterator i = vwp.begin(); i != vwp.end(); ++i)
{
delete *i;
}
}
//解决方法2:将循环变成for_each,将delete变成一个函数对象。
//缺点:需要指定要删除的类型,本例中为
Widget template<typename T>struct DeleteObject: public unary_function<const T*, void> void operator()(const T* ptr) const{
delete ptr;
};
void doSomething()
{
vector<Widget *> vwp;
for (int i = 0; i < size; ++i)
{
vwp.push_back(new Widget);
}
for_each(vwp.begin(), vwp.end(), DeleteObject<Widget>());
}
//这种方法存在的隐患是,如果里面写的是一个基类的对象,则会出现使用基类的指针删除派生类的对象
//当基类没有虚析构函数,则出现不确定行为,如下
class SpecialString : public string{...};//同标准的STL容器一样,string没有虚析构函数,而从没有虚析构函数的类进行公有继承是C++的一项重要禁忌
void doSomthing()
{
deque<SpecialString*> desp;
...
//不确定的行为!通过基类的指针删除派生类对象,而基类又没有虚构函数,因为两个类很像,所以在使用过程中可能出错。
for_each(dssp.begin(), dssp.end(), DeleteObject<string>());
}
//对于这种情况的解决方法是:
//解决方法3:通过让编译器推断出传给DeleteObject::opertator()的指针类型,需要做的是将模板化从DeleteObject移到它的operator()中
struct DeleteObject
{
template<typename T>
void operator()(const T* ptr) const
{
delete ptr;
}
};//这种方法舍弃了可配接的能力 ,调用方法是
void doSomthing()
{
deque<SpecialString*> desp;
...
for_each(dssp.begin(), dssp.end(), DeleteObject());
//编译器知道传给operator()的内容,确定的行为
}
//这种方法仍然不是异常安全的,如果SpecialString已经被创建而对for_each的调用还没有开始时有异常被抛出,则会有资源泄漏发生
//解决这种方法是使用智能指针容器代替指针容器,这里的智能指针通常是指被引用计数的指针//STL本身没有引用计数形式的智能指针,可以使用Boost库中的shared_ptr,则例子应该改写为:
void doSomthing()
{
typedef boost::shared_ptr<Widget> SPW;
vector<SPW> vwp;
for (int i = 0; i < size; ++i)
{
vwp.push_back(SPW(new Widget));
} //从Widget*创建SPW,然后对它进行一次push_back,使用vwp;
}//这里不会有Widget资源泄露,即使在上面的代码中有异常抛出