您的位置:首页 > 其它

STL vector (一)——扩容原理与内存分配与释放

2018-01-05 09:06 393 查看
 vector 常被称为“容器”,习惯将之理解为动态数组。它表示对象的集合,其中所有对象都相同,每个对象都对应一个索引,索引用来访问对象。作为动态数组,vector 有一个指针指向一片连续的内存空间。但是,这个内存空间肯定不是无限大的,当内存装不下数据时,系统会自动申请一片更大的空间,把原来的数据拷贝过去,释放原来的内存空间。具体来看。
      1、内存分配
      vector 里面有 2 个成员函数——size() 和 capacity(),capacity()返回 vector 当前实际申请的空间大小,这部分空间称为缓冲区。而 size() 返回的是当前对象缓冲区元素的个数。显然,capacity() 是大于等于 size()的,当 size() 和capacity() 相等时,如果继续使用 push_back() 添加元素,vector 就会扩容。
      先看一段程序。
int main()
{
vector<int> v1;
for (int i = 1; i < 12; i++)
{
v1.push_back(i);
cout << v1.capacity() << " " << v1.size() << endl;
}

}
看看运行结果:



      我们可以看到,起初,vector 内存是1,然后,每当 capacity() == size() 的时候,就会以 1.5 倍 扩容。1 * 1.5 = 2(以后都是舍去 0.5 ,只有这一次是进1),2+2/2 = 3, 3+3/2 = 4, 4+4/2 = 6, 6+6/2= 9, 9+9/2=13, 依次类推。只要不超过 max_size() ,就可以不断地容纳数据。而 max_size() 是多少呢?看:
cout << v1.max_size() << endl; // 1073741823
      其实,在VS 下,扩容都是以 1.5 倍扩大,但是,在 gcc 编译环境下,是以 2 倍的方式扩容。至于为什么以 1.5 倍或者 2 倍,而不是以3 倍或者3.5倍或者10倍等等,参见我下一篇博客:
      对于 VS 下的源码,只需要右击查看即可,但是这个代码太难看懂了,所以,我选择去看 GCC 下的源代码。如下:
if (_Count == 0)//这里进行了判断,但是什么都不做,不知道为什么???????
;
else if (max_size() - size() < _Count)//编译器可以申请的最大容量也装不下,抛出异常_THROW(length_error, "vector<T> too long");
_Xlen();
else if (_Capacity < size() + _Count)//当前空间不足,需要扩容
{ // not enough room, reallocate
_Capacity = max_size() - _Capacity / 2 < _Capacity
? 0 : _Capacity + _Capacity / 2; // 先保证扩容后的内存大小不超限。如果满足,就扩容50%
if (_Capacity < size() + _Count)// 扩容50%后依然不够容下,则使容量等于当前数据个数加上新增数据个数(有时候是好多数据(存在文件夹)一起push进去
_Capacity = size() + _Count;
pointer _Newvec = this->_Alval.allocate(_Capacity);//申请新的空间
pointer _Ptr = _Newvec;

_TRY_BEGIN
_Ptr = _Umove(_Myfirst, _VEC_ITER_BASE(_Where),
_Newvec); // copy prefix //拷贝原有数据到新的内存中
_Ptr = _Ucopy(_First, _Last, _Ptr); // //拷贝新增数据到新的内存的后面
_Umove(_VEC_ITER_BASE(_Where), _Mylast, _Ptr); // copy suffix
_CATCH_ALL
_Destroy(_Newvec, _Ptr);
this->_Alval.deallocate(_Newvec, _Capacity);//释放原来申请的内存
_RERAISE;
_CATCH_END

    这就是 vector 的内存分配机制。下一部分介绍内存释放
      上一部分笔记,介绍了vector 的扩容机制。这一部分介绍 vector 是如何释放内存的。
      1、析构函数
~vector()
{ // destroy the object
_Tidy();
}

void _Tidy()
{// free all storage
if (_Myfirst != 0)
{// something to free, destroy and deallocate it

#if _HAS_ITERATOR_DEBUGGING
this->_Orphan_all();
#endif /* _HAS_ITERATOR_DEBUGGING */

_Destroy(_Myfirst, _Mylast);//以迭代器的方式,销毁vector中的每一个元素
this->_Alval.deallocate(_Myfirst, _Myend - _Myfirst);//释放缓冲区的空间
}
_Myfirst = 0, _Mylast = 0, _Myend = 0;//指针全部归零
}
我们可以看出,删除容器中的数据的时候,缓冲区的大小并不会改变,而在缓冲区内的数据被清除了。
      我们知道,只有在调用析构函数的时候,vector 才会自动释放缓冲区。那么,如何根据自己的需要,强制释放缓冲区呢?
有两种方法:
// 方法一:
vector<int>().swap(v1);

//方法二:
vector<int> v_temp;
v1.swap(v_temp);分析:方法一是将 v1 直接和空的vector交换,原内存当然就被销毁了。第二种方法是曲线销毁,先定义一个临时变量。由于临时变量没有被初始化,所以,缓冲区大小为0.那么,当 V1 与它交换后,v1 原来占用的缓冲区就被销毁了。而临时变量 v_temp 调用析构函数来进行释放空间。
看代码:

int main()
{
vector<int> v1;
for (int i = 1; i < 11; i++)
{
v1.push_back(i);
cout << "capacity = "<< v1.capacity() << " " <<"size = "<< v1.size() << endl;

}
vector<int>().swap(v1); // 第一种方法
cout << endl << v1.capacity() << endl << endl; // 为 0
}

int main()
{
vector<int> v1;
for (int i = 1; i < 11; i++)
{
v1.push_back(i);
cout << "capacity = "<< v1.capacity() << " " <<"size = "<< v1.size() << endl;

}
vector<int> v_temp; // 第二种方法
cout << v_temp.capacity() << endl; // 为 0
v1.swap(v_temp);
cout << v1.capacity() << endl; // 现在为 0,(原来为13)
cout << v_temp.capacity() << endl; // 现在为 13

v_temp.~vector(); // 显示调用析构函数
cout << v_temp.capacity() << endl; 现在为 0 了

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: