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,你可以像这样来做:

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

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

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

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

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

string s;
...
if (s.size() < s.capacity()) {
s.push_back('x');
}

  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所占内存

template < class T> void ClearVector( vector<T>& v )
{
vector<T> vtTemp;
vtTemp.swap( v );
}

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

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

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

#include <iostream>
#include <vector>
using namespace std; int main()
{
vector<int> iVec;
cout << "容器 大小为: " << iVec.size() << endl;//暂时数据项保存,但是有1个容量
cout << "容器 容量为: " << iVec.capacity() << endl; //1个元素, 容器容量为1 iVec.push_back();
cout << "容器 大小为: " << iVec.size() << endl;//有1个数据项
cout << "容器 容量为: " << iVec.capacity() << endl; //2个元素, 容器容量为2,size == capacity ,容量范围 容量X2 iVec.push_back();
cout << "容器 大小为: " << iVec.size() << endl;//有2个数据项
cout << "容器 容量为: " << iVec.capacity() << endl; //3个元素, 容器容量为4 iVec.push_back();
cout << "容器 大小为: " << iVec.size() << endl;//有3个数据项
cout << "容器 容量为: " << iVec.capacity() << endl; //4个元素, 容器容量为4,(3 + 1 == 4 ,即size == capacity ,容量范围 容量X2 iVec.push_back();
iVec.push_back();
cout << "容器 大小为: " << iVec.size() << endl;
cout << "容器 容量为: " << iVec.capacity() << endl; //5个元素, 容器容量为8 iVec.push_back();
cout << "容器 大小为: " << iVec.size() << endl;
cout << "容器 容量为: " << iVec.capacity() << endl; //6个元素, 容器容量为8 iVec.push_back();
cout << "容器 大小为: " << iVec.size() << endl;
cout << "容器 容量为: " << iVec.capacity() << endl; //7个元素, 容器容量为8 iVec.push_back();
cout << "容器 大小为: " << iVec.size() << endl;
cout << "容器 容量为: " << iVec.capacity() << endl; //8个元素, 容器容量为8 (size == capacity ,容量范围 容量X2) iVec.push_back();
cout << "容器 大小为: " << iVec.size() << endl;
cout << "容器 容量为: " << iVec.capacity() << endl; //9个元素, 容器容量为16
/* vs2005/8 容量增长不是翻倍的,如
9个元素 容量9
10个元素 容量13 */ /* 测试effective stl中的特殊的交换 swap() */
cout << "当前vector 的大小为: " << iVec.size() << endl;
cout << "当前vector 的容量为: " << iVec.capacity() << endl;
vector<int>(iVec).swap(iVec); cout << "临时的vector<int>对象 的大小为: " << (vector<int>(iVec)).size() << endl;
cout << "临时的vector<int>对象 的容量为: " << (vector<int>(iVec)).capacity() << endl;
cout << "交换后,当前vector 的大小为: " << iVec.size() << endl;
cout << "交换后,当前vector 的容量为: " << iVec.capacity() << endl; return ;
}

5. vector的其他成员函数

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

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. sqlserver解密加密的存储过程(图解)

    来自博客:http://www.cnblogs.com/wghao/archive/2012/12/30/2837642.html

  2. js 删除节点,jquery遍历通过内容定位节点

    $(".class1 .class2").each(function (index, item) { var gettedValue = $(item).find(".c ...

  3. Oracle Client安装报错

    Oracle Client安装报错:引用数据不可用于验证此操作系统分发的先决条件 http://tunps.com/p/11797.html 原因是Oracle Client 11g版本不支持最新的W ...

  4. Linux Tomcat重新启动

    在Linux系统下,重启Tomcat使用命令操作的! 首先,进入Tomcat下的bin目录 cd /usr/local/tomcat/bin 使用Tomcat关闭命令 ./shutdown.sh 查看 ...

  5. crsf 跨站请求伪造

    [crsf 跨站请求伪造] CSRF(Cross-site request forgery),中文名称:跨站请求伪造.核心为利用浏览器帮助提交cookie.采用随机数方可防御.估计大部小站均无CSRF ...

  6. VirtualBox中的虚拟机在Ubuntu 下无法启动之问题解决

    我通过重新安装box解决的.发现,box安装,直接apt install有可能会出现虚拟机无法启动的问题. 一.添加VirtualBox的源并安装5.1版本virtualbox官网:https://w ...

  7. Mybatis的分页插件PageHelp:Page对象中的pageSize等属性无法序列化,无法转换为json字符串

    Page<User> page = new Page<>();        User user = new User();        user.setAge(20);   ...

  8. windows常用的cmd网络命令

    一.ping 它是用来检查网络是否通畅或者网络连接速度的命令.作为一个生活在网络上的管理员或者黑客来说,ping命令是第一个必须掌握的DOS命令,它所利用的原理是这样的:网络上的机器都有唯一确定的IP ...

  9. PAT 1020 月饼 (25)(精简版代码+思路+推荐测试用例)

    1020 月饼 (25)(25 分)提问 月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼.现给定所有种类月饼的库存量.总售价.以及市场的最大需求量,请你计算可以获得的最大收益是 ...

  10. 如何在win 2008 server和win 7上add web site

    在 windows 2008  server 英文版的操作系统上,通过桌面上的 Computer 右键选择 Manage ,打开 Server Manager,选中左侧资源树中的Roles 在上图右侧 ...