STL中的内存与效率

1. 使用reserve()函数提前设定容量大小,避免多次容量扩充操作导致效率低下。

   关于STL容器,最令人称赞的特性之一就是是只要不超过它们的最大大小,它们就可以自动增长到足以容纳你放进去的数据。(要知道这个最大值,只要 调用名叫max_size的成员函数。)对于vector和string,如果需要更多空间,就以类似realloc的思想来增长大小。

  vector容器 支持随机访问,因此为了提高效率,它内部使用动态数组的方式实现的。

  在通过 reserve() 来申请特定大小的时候总是按指数边界来增大其内部缓冲区。

  当进行insert或push_back等增加元素的操作时,如果此时动态数组的内存不够用,就 要动态的重新分配当前大小的1.5~2倍的新内存区,再把原数组的内容复制过去。所以,在一般情况下,其访问速度同一般数组,只有在重新分配发生时,其性 能才会下降。正如上面的代码告诉你的那样。

  而进行pop_back操作时,capacity并不会因为vector容器里的元素减少而有所下降,还会维持 操作之前的大小。对于vector容器来说,如果有大量的数据需要进行push_back,应当使用reserve()函数提前设定其容量大小,否则会出 现许多次容量扩充操作,导致效率低下。

reserve()成员函数允许你最小化必须进行的重新分配的次数,因而可以避免真分配的开销和迭代器/指针/引用失效。但在我解释reserve为什么可以 那么做之前,让我简要介绍有时候令人困惑的四个相关成员函数。

  在标准容器中,只有vector和string提供了所有这些函数。

  (1) size()告诉你容器中有多少元素。它没有告诉你容器为它容纳的元素分配了多少内存。 
  (2)
capacity()告诉你容器在它已经分配的内存中可以容纳多少元素。那是容器在那块内存中总共可以容纳多少元素,而不是还可以容纳多少元素。

    如果你想
知道一个vector或string中有多少没有被占用的内存,你必须从capacity()中减去size()。如果size和capacity返回同
样的值,容器中就没有剩余空间了,而下一次插入(通过insert或push_back等)会引发上面的重新分配步骤。 
  (3) resize(Container::size_type
n)强制把容器改为容纳n个元素。调用resize之后,size将会返回n。如果n小于当前大小,容器尾部的元素会被销毁。如果n大于当前大小,新默认
构造的元素会添加到容器尾部。如果n大于当前容量,在元素加入之前会发生重新分配。 
  (4) reserve(Container::size_type
n)强制容器把它的容量改为至少n,提供的n不小于当前大小。这一般强迫进行一次重新分配,因为容量需要增加。(如果n小于当前容量,vector忽略
它,这个调用什么都不做,string可能把它的容量减少为size()和n中大的数,但string的大小没有改变。

  在我的经验中,使用reserve
来从一个string中修整多余容量一般不如使用“交换技巧”,那是条款17的主题。)

  这个简介表示了只要有元素需要插入而且容器的容量不足时就会发生重新分配(包括它们维护的原始内存分配和回收,对象的拷贝和析构和迭代器、指针和引用的失效)。

  所以,避免重新分配的关键是使用reserve尽快把容器的容量设置为足够大,最好在容器被构造之后立刻进行。

  例如,假定你想建立一个容纳1-1000值的vector<int>。没有使用reserve,你可以像这样来做:

  1. vector<int> v;
  2. for (int i = ; i <= ; ++i) v.push_back(i);

  在大多数STL实现中,这段代码在循环过程中将会导致2到10次重新分配。(10这个数没什么奇怪的。记住vector在重新分配发生时一般把容量翻倍,而1000约等于210。)

  把代码改为使用reserve,我们得到这个:

  1. vector<int> v;
  2. v.reserve();
  3. for (int i = ; i <= ; ++i) v.push_back(i);
  4. 这在循环中不会发生重新分配。

  在大小和容量之间的关系让我们可以预言什么时候插入将引起vector或string执行重新分配,而且,可以预言什么时候插入会使指向容器中的迭代器、指针和引用失效。例如,给出这段代码,

  1. string s;
  2. ...
  3. if (s.size() < s.capacity()) {
  4. s.push_back('x');
  5. }

  push_back的调用不会使指向这个string中的迭代器、指针或引用失效,因为string的容量保证大于它的大小。如果不是执行 push_back,代码在string的任意位置进行一个insert,我们仍然可以保证在插入期间没有发生重新分配,但是,与伴随string插入时 迭代器失效的一般规则一致,所有从插入位置到string结尾的迭代器/指针/引用将失效。

  回到本条款的主旨,通常有两情况使用reserve来避免不必要的重新分配。

  (1)第一个可用的情况是当你确切或者大约知道有多少元素将最后出现在容器中。那样的话,就像上面的vector代码,你只是提前reserve适当数量的空间。

  (2)第二种情况是保留你可能需要的最大的空间,然后,一旦你添加完全部 数据,修整掉任何多余的容量。

2. 使用“交换技巧”来修整vector过剩空间/内存

  有一种方法来把它从曾经最大的容量减少到它现在需要的容量。

  这样减少容量的方法常常被称为“收缩到合适(shrink to fit)”。该方法只需一条语句:vector<int>(ivec).swap(ivec)。

  表达式vector<int>(ivec)建立一个临时vector,它是ivec的一份拷贝:vector的拷贝构造函数做了这个工作。但 是,vector的拷贝构造函数只分配拷贝的元素需要的内存,所以这个临时vector没有多余的容量。然后我们让临时vector和ivec交换数据, 这时我们完成了,ivec只有临时变量的修整过的容量,而这个临时变量则持有了曾经在ivec中的没用到的过剩容量。在这里(这个语句结尾),临时 vector被销毁,因此释放了以前ivec使用的内存,收缩到合适

3. 用swap方法强行释放STL Vector所占内存

  1. template < class T> void ClearVector( vector<T>& v )
  2. {
  3. vector<T> vtTemp;
  4. vtTemp.swap( v );
  5. }

  6. vector<int> v ;
  7. nums.push_back();
  8. nums.push_back();
  9. nums.push_back();
  10. nums.push_back();
  11. vector<int>().swap(v);
  12.  
  13. /* 或者v.swap(vector<int>()); */
  14.  
  15. /*或者{ std::vector<int> tmp = v; v.swap(tmp); };
    //加大括号{ }是让tmp退出{ }时自动析构*/,因为退出了临时变量的作用域

4. Vector 内存管理成员函数的行为测试

  C++ STL的vector使用非常广泛,但是对其内存的管理模型一直有多种猜测,下面用实例代码测试来了解其内存管理方式,测试代码如下:

  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4.  
  5. int main()
  6. {
  7. vector<int> iVec;
  8. cout << "容器 大小为: " << iVec.size() << endl;//暂时数据项保存,但是有1个容量
  9. cout << "容器 容量为: " << iVec.capacity() << endl; //1个元素, 容器容量为1
  10.  
  11. iVec.push_back();
  12. cout << "容器 大小为: " << iVec.size() << endl;//有1个数据项
  13. cout << "容器 容量为: " << iVec.capacity() << endl; //2个元素, 容器容量为2,size == capacity ,容量范围 容量X2
  14.  
  15. iVec.push_back();
  16. cout << "容器 大小为: " << iVec.size() << endl;//有2个数据项
  17. cout << "容器 容量为: " << iVec.capacity() << endl; //3个元素, 容器容量为4
  18.  
  19. iVec.push_back();
  20. cout << "容器 大小为: " << iVec.size() << endl;//有3个数据项
  21. cout << "容器 容量为: " << iVec.capacity() << endl; //4个元素, 容器容量为4,(3 + 1 == 4 ,即size == capacity ,容量范围 容量X2
  22.  
  23. iVec.push_back();
  24. iVec.push_back();
  25. cout << "容器 大小为: " << iVec.size() << endl;
  26. cout << "容器 容量为: " << iVec.capacity() << endl; //5个元素, 容器容量为8
  27.  
  28. iVec.push_back();
  29. cout << "容器 大小为: " << iVec.size() << endl;
  30. cout << "容器 容量为: " << iVec.capacity() << endl; //6个元素, 容器容量为8
  31.  
  32. iVec.push_back();
  33. cout << "容器 大小为: " << iVec.size() << endl;
  34. cout << "容器 容量为: " << iVec.capacity() << endl; //7个元素, 容器容量为8
  35.  
  36. iVec.push_back();
  37. cout << "容器 大小为: " << iVec.size() << endl;
  38. cout << "容器 容量为: " << iVec.capacity() << endl; //8个元素, 容器容量为8 (size == capacity ,容量范围 容量X2)
  39.  
  40. iVec.push_back();
  41. cout << "容器 大小为: " << iVec.size() << endl;
  42. cout << "容器 容量为: " << iVec.capacity() << endl; //9个元素, 容器容量为16
  43. /* vs2005/8 容量增长不是翻倍的,如
  44. 9个元素 容量9
  45. 10个元素 容量13 */
  46.  
  47. /* 测试effective stl中的特殊的交换 swap() */
  48. cout << "当前vector 的大小为: " << iVec.size() << endl;
  49. cout << "当前vector 的容量为: " << iVec.capacity() << endl;
  50. vector<int>(iVec).swap(iVec);
  51.  
  52. cout << "临时的vector<int>对象 的大小为: " << (vector<int>(iVec)).size() << endl;
  53. cout << "临时的vector<int>对象 的容量为: " << (vector<int>(iVec)).capacity() << endl;
  54. cout << "交换后,当前vector 的大小为: " << iVec.size() << endl;
  55. cout << "交换后,当前vector 的容量为: " << iVec.capacity() << endl;
  56.  
  57. return ;
  58. }

5. vector的其他成员函数

  1. c.assign(beg,end)
  2. 将[beg; end)区间中的数据赋值给c
  3. c.assign(n,elem)
  4. nelem的拷贝赋值给c
  5. c.at(idx)
  6. 传回索引idx所指的数据,如果idx越界,抛出out_of_range
  7. c.back()
  8. 传回最后一个数据,不检查这个数据是否存在。
  9. c.front()
  10. 传回地一个数据。
  11. get_allocator
  12. 使用构造函数返回一个拷贝。
  13. c.rbegin()
  14. 传回一个逆向队列的第一个数据。
  15. c.rend()
  16. 传回一个逆向队列的最后一个数据的下一个位置。
  17. c.~ vector <Elem>()
  18. 销毁所有数据,释放内存。

STL中的内存与效率的更多相关文章

  1. C++的STL中vector内存分配方法的简单探索

    STL中vector什么时候会自动分配内存,又是怎么分配的呢? 环境:Linux  CentOS 5.2 1.代码 #include <vector> #include <stdio ...

  2. SGI STL中内存池的实现

    最近这两天研究了一下SGI STL中的内存池, 网上对于这一块的讲解很多, 但是要么讲的不完整, 要么讲的不够简单(至少对于我这样的初学者来讲是这样的...), 所以接下来我将把我对于对于SGI ST ...

  3. 自己动手实现STL 01:内存配置器的实现(stl_alloc.h)

    一.前言 在STL中,容器是其中的重中之重,基本的STL中的算法,仿函数等都是围绕着容器实现的功能.而,内存配置器,是容器的实现的基础.所以,我第一次要去编写便是内存配置器的实现.在STL中,内存配置 ...

  4. C++ STL中的map用红黑树实现,搜索效率是O(lgN),为什么不像python一样用散列表从而获得常数级搜索效率呢?

    C++ STL中的标准规定: map, 有序 unordered_map,无序,这个就是用散列表实现 谈谈hashmap和map的区别,我们知道hashmap是平均O(1),map是平均O(lnN)的 ...

  5. (转)C++ STL中的vector的内存分配与释放

    C++ STL中的vector的内存分配与释放http://www.cnblogs.com/biyeymyhjob/archive/2012/09/12/2674004.html 1.vector的内 ...

  6. STL中的set容器的一点总结

    1.关于set C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构 ...

  7. STL库的内存配置器(allocator)

    正在学习中,如果有错,还请多多指教,根据不断的理解,会进行更改,更改之前的样子都会保留下来,记录错误是最大的进步,嗯嗯! 具有次配置力的SGI空间配置器(SGI是STL的一种版本,也有其他的版本) 这 ...

  8. windows进程中的内存结构[转载]

    在阅读本文之前,如果你连堆栈是什么多不知道的话,请先阅读文章后面的基础知识. 接触过编程的人都知道,高级语言都能通过变量名来访问内存中的数据.那么这些变量在内存中是如何存放的呢?程序又是如何使用这些变 ...

  9. 深入了解STL中set与hash_set,hash表基础

    一,set和hash_set简介 在STL中,set是以红黑树(RB-Tree)作为底层数据结构的,hash_set是以哈希表(Hash table)作为底层数据结构的.set可以在时间复杂度为O(l ...

随机推荐

  1. redmine邮件配置

    网上找了半天,有很多答案,最后自己测试找出一个解决办法. 1.找到安装位置 D:\Bitnami\redmine-2.5.2-2\apps\redmine\htdocs\config下的文件confi ...

  2. RESTORE 无法处理数据库 'Students',因为它正由此会话使用。建议在执行此操作时使用 master 数据库。

    恢复数据库是总弹出报错对话框如下:RESTORE 无法处理数据库 'Students',因为它正由此会话使用.建议在执行此操作时使用 master 数据库.RESTORE DATABASE 正在异常终 ...

  3. select 中添加option的注意

    在平时写JS中经常要给Select添加option,如果我们把option中的数据用一个字符串来表示: eg: var strOption='<option>1</option> ...

  4. Java中static代码块,{}大括号代码块,构造方法代码块执行顺序!

    注:下列代码中的注释都是JUnit4单元测试运行结果. 首先,没有父类的(父类是Object)的类A package Static.of; public class A { { System.out. ...

  5. 使用sizemask来确保索引不越界

    [使用sizemask来确保索引不越界] 在redis的字典实现中,有一个sizemask字段. 此字段的作用是当使用下标访问数据时,确保下标不越界. 如当前size为8时,sizemask为7(0x ...

  6. 根据数组下标在MongoDB中修改数组元素

    如下图这样的数据: 即文档中某个字段是一个数组,而每个数组元素又是一个对象,现在需求是对每个对象中的content字段值作情感分析后,把情感分析得到的结果增加到这个对象中去. 如上图中第1个元素,修改 ...

  7. 通过Chrome的inspect对手机webview进行调试

    使用chrome的inspect可以对手机上的webview进行调试,因为真机没有什么比较好的调试工具,而chrome提供了这一个工具可以比较方便的查看真机上的元素,以及进行调试. 其实我对webvi ...

  8. 第七章 二叉搜索树 (d3)AVL树:删除

  9. sqlserver 几种datatime的区别

    参考文章1 smalldatetime 占4位精确到分钟.时间从1900.1.1到2079.6.6datetime占8位精确到毫秒.时间从1753.1.1到9999.12.31 参考文章2 datet ...

  10. 10-能不能组成m

    /*                                找数达人 时间限制:1000 ms  |  内存限制:65535 KB      难度:2 描述 小明最近做出了一道题:如何在一组数 ...