Effective stl 第一章 第7、8条

9 篇文章 0 订阅

第7条  如果容器中包含了通过new操作创建的指针,切记在容器对象析构前将指针delete掉。

如果一个容器包含指针,则析构函数不对指针做任何事情,不会对指针所指的对象做delete操作,所以会引起内存泄露

对于解决方法,文章采用一步一步引进的方式,由一个方法引出另一个方法,

如用代码写出:

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资源泄露,即使在上面的代码中有异常抛出 


永远不要错误的认为:你可以通过创建auto_ptr的容器使指针被自动删除

需要记住的是:STL容器很智能,但没有智能到知道是否该删除自己所包含的指针的程度。当你使用指针的容器时,而其中的指针应该被删除时,为了避免资源泄漏,

1、使用引用计数形式的智能指针对象(boost的shared_ptr)代替指针

2、当容器被析构时手工删除其中的每个指针


第8条:切勿创建包含auto_ptr的容器对象

auto_ptr的容器是被禁止的。

当你复制一个auto_ptr时,它所指向的对象的所有权被移交到拷入的auto_ptr上,而它自身被设置为NULL,可以理解为复制一个auto_ptr意味着改变它的值

例子:

auto_ptr<Widget>  pw1(new Widget);      //pw1指向一个Widget
auto_ptr<Widget> pw2(pw1) ;                  //pw2指向pw1的Widget:pw1被置为NULL(Widget的所有权从pw1转移到pw2上)
pw1 = pw2;        //现在pw1又指向Widget了:pw2被置为NULL 

//对该类的指针的排序准则
bool widgetAPCompare(const auto_ptr<Widget>&lhs,
					 const auto_ptr<Widget>&rhs)
{
	return *lhs < *rhs;   //假定对Widget存在operator<操作符
}

vector<auto_ptr<Widget> > widgets;     //不应该
...
//这个操作是不对的,因为排序过程中,有赋值操作,会将widgets中的一个
//或多个auto_ptr可能被置为NULL,对vector进行排序可能会改变它的内容
//这可以从读sort的源码看出来
sort(widgets.begin(), widgets.end(), widgetAPCompare);

所以不要使用包含auto_ptr的容器



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值