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 了
}
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 了
}
相关文章推荐
- 频繁分配释放内存导致的性能问题的分析 --(附)malloc分配原理浅析 mmap关注焦点 如何优化分配内存
- (转)!!频繁分配释放内存导致的性能问题的分析 --(附)malloc分配原理浅析 mmap关注焦点 如何优化分配内存
- Netty学习之旅----源码分析内存分配与释放原理
- c与c++分别是怎样动态分配和释放内存的,有什么区别?
- C与C++动态分配,释放内存的区别
- Java中堆栈常量池等内存分配原理详解
- Java内存分配原理 (3)
- STL中vector的内存分配与释放
- 深入Java核心 Java内存分配原理精讲
- 使用二级指针分配和释放内存示例 malloc(), free()
- C++ STL中的vector的内存分配与释放
- [转]Java核心内存分配原理精讲
- 三十二、数组、指针和字符串:动态内存分配和释放
- DLL分配的内存如何在EXE里面释放
- delphi 精要-读书笔记(内存分配释放)
- Windows编程 在一个模块中分配的内存在另外一个模块释放?
- Linux中的内存分配和释放之free_bootmem()函数分析
- 内存管理概述、内存分配与释放、地址映射机制
- 内存分配与释放
- 不显示删除回复显示所有回复显示星级回复显示得分回复 [推荐] [百度分享]频繁分配释放内存导致的性能问题的分析