Effective STL(2)

来源:互联网 发布:西瓜影音mac版下载 编辑:程序博客网 时间:2024/05/21 11:18
  • 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
原创粉丝点击