Effective STL(2)

  • Rule7:当使用new得指针的容器时,记得销毁容器前delete那些指针

C++ 中STL的容器的析构函数不用自己调用,系统会进行析构,但是vector内元素的清空需要手动进行。
1. 非指针的数据类型,比如int,string,char,还包括自定义的数据结构、自定义的类等等,只需要手动调用vector的clear函数就行,空间的释放和系统系统就会自动进行。
2. 指针类型的数据,这种情况需要手动进行释放,也就说new产生的内存需要手动使用delete进行释放。

这一部分需要详细进行讲解:
首先将 在VS开发中检测内存泄露的函数,
_CrtDumpMemoryLeaks();
这个函数放在你需要检测内存段的后面,也就说他可以自动检测在它代码前的所有程序内存泄露情况。

举一个例子:

void test()
{
    vector<Widget*> vpw;

    for (int i=0;i<1000;i++)
    {
        vpw.push_back(new Widget);
    }
    vpw.clear();
}

int _tmain(int argc, _TCHAR* argv[])
{
test();
_CrtDumpMemoryLeaks();
return 0;
}

在vs中程序能够运行,没有报错,但是可以很好看出,vector里面数据是指针类型,需要手动释放指针所指向的数据。否则里面的数据就会变成野数据,没有释放内存资源。
如果不通过_CrtDumpMemoryLeaks()可能你得不到任何提示。
程序运行效果如下:
这里写图片描述
在“输出”选项卡中我们截取了一部分内存泄露的输出显示。
我们说过vector本身的析构函数是在作用域结束后自动调用的,我们看在如下的代码:

int _tmain(int argc, _TCHAR* argv[])
{
vector<int> vpw;

    for (int i=0;i<1000;i++)
    {
        vpw.push_back(i);
    }
vpw.clear();
_CrtDumpMemoryLeaks();
return 0;
}

vector里面的元素是基本元素类型,理应与内存泄露没有任何联系,我们不用去理会。
但是:这是程序的output“输出”选项卡的内容:
这里写图片描述
这里面出现了两次内存泄露,可以直接分析出 这包括了所有int元素,及vector变量本身。
??为什么呢?
因为 vector释放内存的时间是失去作用域后,但是如上时刻调用_CrtDumpMemoryLeaks()没有失去作用域呀!!!。为了测试:
可以把上述代码封装到一个测试函数中或者加上{}作为一个内部作用域。

我们再回到带有指针的vector内存释放的问题上来,当我们调用vector.clear()时,内部的指针数据的确是自动释放了,但是,指针所指向的数据却没有被释放掉,这一部分需要手动进行释放,毕竟他不能自己自动调用delete方法。
修正后的操作如下:

void test()
{
    vector<Widget*> vpw;

    for (int i=0;i<1000;i++)
    {
        vpw.push_back(new Widget);
    }

for(vector<Widget*>::iterator it = vpm.begin();it!=vpm.end();it++)
{
    delete *it;
    *it = NULL;
}
    vpw.clear();
}

在看如下的代码:
这里写图片描述
通过调用clear()函数,vector的存储量没有发生改变。
swap()是交换函数,使vector离开其自身的作用域,从而强制释放vector所占的内存空间,总而言之,释放vector内存最简单的方法是vector< int >.swap(nums)。
容器中的成员函数swap
在容器vector中,其内存占用的空间是只增不减的,比如说首先分配了10,000个字节,然后erase掉后面9,999个,则虽然有效元素只有一个,但是内存占用仍为10,000个。所有内存空间在vector析构时回收。
一般,我们都会通过vector中成员函数clear进行一些清除操作,但它清除的是所有的元素,使vector的大小减少至0,却不能减小vector占用的内存。要避免vector持有它不再需要的内存,这就需要一种方法来使得它从曾经的容量减少至它现在需要的容量,这样减少容量的方法被称为“收缩到合适(shrink to fit)”。(节选自《Effective STL》)如果做到“收缩到合适”呢,此时就需要使用swap了。

STL本身没有包含引用计数指针,智能指针出错的不同方式有很多,一个经过检验后的智能指针是Boost库中的shared_ptr,引用Boost中的shared_ptr,可以这样使用:

typedef boost::shared_ptr<Widget> SPW;

vector<SPW> vwp;
for(int i=0;i<SIZE;i++)
    vwp.push_back(SPW(new Widget));

如果需要使用引用指针,你是不能够使用auto_ptr这个智能指针的。如果不需要引用指针,我们可以使用auto_ptr,比如如下代码也不会发生内存泄露的问题。

    typedef auto_ptr<Widget> WAP;
    vector<WAP> vpw;
    for (int i=0;i<1000;i++)
    {
        vpw.push_back(WAP(new Widget));
    }

我们需要知道的是所STL容器很智能,但没有智能到知道时候应该删除它们所包含的指针,当你要删除指针的容器时要避免资源泄漏,你必须用智能引用计数指针对象,比如Boost的shared_ptr来替代指针,或者你必须在容器销毁前手动删除容器中的每个指针。

  • Rule8:永不建立auto_ptr的容器
    前面我们都提到过auto_ptr,但是我们看在Effective STL中作者对于auto_ptr的看法。
    auto_ptr的容器(COAPs)是禁止的,试图使用它们的代码都不能编译,C++ 标准委员会花费了无数努力来安排这种情况。
    COAPs不可移植。
    C++标准禁止他们,比较好的STL平台已经实现了。可以有足够理由推断随着时间的推移,目前不能实现标准的这个方面的STL平台将变得更适应,并且当那发生时,使用COAPs的代码将更比现在更不可移植。

当然如果你不用考虑移植性的话,auto_ptr所指向对象的所有权被转移到拷贝的auto_ptr上,而被拷贝的auto_ptr被设为NULL,拷贝一个auto_ptr将改变他的值。

这非常不寻常,也许它很有趣,但你(作为STL的用户)关心的原因是它导致一些非常令人惊讶的行为。例如,考虑这段看起来很正确的代码,它建立一个auto_ptr< Widget >的vector,然后使用一个比较指向的Widget的值的函数对它进行排序。

bool widgetAPCompare(const auto_ptr<Widget>& lhs, 
            const auto_ptr<Widget>& rhs) {
    return *lhs < *rhs;     // 对于这个例子,假设Widget
}                   // 存在operator<

vector<auto_ptr<Widget> > widgets;      // 建立一个vector,然后用Widget的auto_ptr填充它;记住这将不能编译!
sort(widgets.begin(), widgets.end(),// 排序这个vector 
widgetAPCompare);

如果在代码中需要把一个元素从保存的区间拷贝到局部临时对象中,就会发生不可预知的问题,因为一旦发生拷贝,原始的auto
_ptr就会变成NULL,再次使用就会出现问题。
这就是为什么标准委员会会建议永不建立auto_ptr的内容,即使你的STL平台允许你那么做。比如VS中支持auto_ptr的使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值