vector内存分配和回收机制

随着对C++的深入学习,逐渐发现了一些很重要的知识点。本文重点讲解C++ Vector内存机制和效率问题。

iOS移动端

记得之前做iOS移动端时,关于垃圾回收机制是这样说的:

ARC的基本概念:

      ARC(AutoRefrenceCount)自动引用计数,当你在编译程序时提供自动管理内存的功能,它会自动加入内存的控制代码,控制对象的生命周期。这是在iOS4以后引入的技术。此时你在代码中使用release,retain时就会报错,也就是这些方法被禁用。当然你也可以在项目中进行设置,选择重新启用MRC(手动内存管理)。在Build Setting里面把“Objective-C Automatic Reference Counting”设置为NO,就可以不启用ARC。

垃圾回收机制的基本概念:

     与Java、.net语言相同,Objective-C2.0以后,也提供了垃圾回收机制。但在iOS移动终端设备中,并不支持垃圾回收机制(取决于终端设备的性能)。因此,iPhone并不能对内存进行自动垃圾回收机制(取决于终端设备的性能)。因此,iPhone并不能对内存进行自动垃圾回收处理(中间模式autorelease)。我们需要注意垃圾回收机制并不是ARC,ARC也是需要管理内存的,只不过是隐式的管理内存,编译器会在适当的地方自动插入retain,release和autorelease.

回到正题

C++ Vector中内存分配和回收机制

1.内存增长

vector所有的内存相关问题都可以归结于它的内存增长策略。vector有一个特点就是:内存空间只会增长不会减少。vector有两个函数,一个是capacity(),返回对象缓冲区(vector维护的内存空间)实际申请的空间大小,另一个size(),返回当前对象缓冲区存储数据的个数。对于vector来说,capacity是永远大于等于size的,但capacity和size相等时,vector就会扩容,capacity变大。

比如说vector最常用的push_back操作,它的整个过程是怎么一个机制呢?这个问题经常在面试中出现。

这个问题其实很简单,在调用 push_back时,若当前容量已经不能够放入新的元素(capacity=size),那么vector会重新申请一块内存,把之前的内存里的元素拷贝到新的内存当中,然后把push_back的元素拷贝到新的内存中,最后要析构原有的vector并释放原有的内存。所以说这个过程的效率是极低的,为了避免频繁的分配内存,C++每次申请内存都会成倍的增长,例如之前是4,那么重新申请后就是8,以此类推。当然不一定是成倍增长,比如在我的编译器环境下实测是0.5倍增长,之前是4,重新申请后就是6。

2. 内存释放

就像前面所说的,vector的内存空间是只增加不减少的,我们常用的操作clear()和erase(),实际上只是减少了size(),清除了数据,并不会减少capacity,所以内存空间没有减少。那么如何释放内存空间呢,正确的做法是swap()操作。

标准模板如下

[cpp]  view plain  copy
  1. template < class T >  
  2. void ClearVector( vector< T >& vt )   
  3. {  
  4.     vector< T > vtTemp;   
  5.     veTemp.swap( vt );  
  6. }  



也可以简单使用以下操作

[cpp]  view plain  copy
  1. vector<Point>().swap(pointVec); //或者pointVec.swap(vector<Point> ())  



swap交换技巧实现内存释放思想:vector()使用vector的默认构造函数建立临时vector对象,再在该临时对象上调用swap成员,swap调用之后原来vector占用的空间就等于一个默认构造的对象的大小,临时对象就具有原来对象v的大小,而该临时对象随即就会被析构,从而其占用的空间也被释放。

[cpp]  view plain  copy
  1. std::vector<T>().swap(X)  
  2. 作用相当于:  
  3. {  
  4. std::vector<T>  temp(X);  
  5. temp.swap(X);  
  6. }  


交换之后,temp会被析构。

3.总结

由上可见,vector虽然是动态数组,但是本质上和数组没什么区别,频繁的销毁新建,效率很低,所以正确的做法是新建vector的时候初始化一个合适的大小(),回到了数组的老路上。不过之后可以动态变化还是很方便,而且还有很多好用的函数。


vecotr是动态数组,顾名思义他可以动态的增加自己的长度。

内存机制:

但是怎样的增加自己的长度?

     vector有两个函数一个是capacity()返回内存空间即缓冲区的大小,另一个是size()返回当前数组中数的数量。vector增加元素来说,当容量已经不能放进数据了,那么他会重新申请一块内存,把之前的内存利用复制构造函数复制到新的内存当中,然后把新添加的内容放入后面,另外此时的他申请的内存空间是原来空间的2倍,我测得是2倍

 参考文章:http://www.cnblogs.com/zhaopAC/p/5404828.html

     缓冲区的释放

     vecotor占用的内存只增不减,erase只是将一段区间( earse(arr.begin(), arr.end() )的值清除掉或者将某个值( erase(x) )清除掉,但是对于内存空间来说是没变化的

所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。

复制代码
方法一:
vecotr<Type>().swap(arr) // 回收arr内存
方法二:
模板:
Template < class T >
void ClearVector( vector< T >& vt ) 
{
    vector< T > vtTemp; 
    veTemp.swap( vt );
}
调用模板函数
void ClearVector<vector< T> & vt)
{  
    vector<int> temp;//临时对象未初始化,其缓冲区大小为0,没有数据  
    arr.swap(temp);//与我们的对象交换数据,arr的缓冲区就没了。  
}//临时变量会被析构,temp调用vector析构函数释放空间  
复制代码

----------------------------------------------------------------------------

性能分析:

主要比较三种插入方式:

1、直接push_back() 2、 reserve(n) 预定n个空间,当然后续push_back()会增加,其中的值不确定, 3 resize(n, x)  申请n个空间初始化为x,

reserve只是保持一个最小的空间大小,而resize则是对缓冲区进行重新分配,里面涉及到的判断和内存处理比较多所以比reserve慢一些,对于数据数目可以确定的时候,先预设空间大小是很有必要的。直接push_back数据频繁移动很是耗时



猜你喜欢

转载自blog.csdn.net/aidam_bo/article/details/80898404
今日推荐