目录
2)全部清空可以考虑:vector().swap(vec);
3)vector tmpVec(vec);这样创建出来的对象是没有空闲空间的。
4)vector(vec).swap(vec);释放过剩的容量
- size :vector中元素个数
- capacity :vector的容量,即开辟空间大小。
- push_back:当向vector中放入元素时,若size == capacity,此时vector会重新申请一段内存是之前内存的2倍,然后将原来的元素拷贝到新的里边,此时指向原来vector的迭代器也会失效。而不断的push_back,会导致不断的拷贝操作,影响效率。
- resize(n): 改变size 的大小(改变vector中元素的数目),并给新的元素赋值
- reserve(n):只是改变capacity的大小(当n<原来capacity时,size和capacity不会变)
- std::vector::swap:交换两个vector,仅仅是交换了指向的首尾指针和容量指针。
一、vector的push_back解析
vector容器的底层实现基于数组,vector的核心就是动态分配内存。下面解析push_back函数:
当数组中新增加一个元素x时,先判断是否还有备用空间:有,将当前指针的值设为x,并将当前指针+1;如果备用空间已经用完,如果之前空间为0,则重新分配大小为1的空间,否则将空间扩容为之前的两倍,然后将旧容器中的值重新拷贝到新空间中,并重新分配起始指针和当前指针。所以使用vector需要注意的一点就是尽量不要动态给它分配空间。而且空间重新分配之后,之前的所有指针都会失效(特别要注意)。
vector扩容机制取决于编译器类型:VS2015-----1.5倍扩容;g++编译器-------2倍扩容
具体实现:
void push_back(const T& x) {
if (finish != end_of_storage) { //若当前还有备用空间
construct(finish, x); //将当前水位的值设为x
++finish; //提升水位
}
else
insert_aux(end(), x);
}
template <class T, class Alloc>
void vector<T, Alloc>::insert_aux(iterator position, const T& x) {
if (finish != end_of_storage) {
construct(finish, *(finish - 1));
++finish;
T x_copy = x;
copy_backward(position, finish - 2, finish - 1);
*position = x_copy;
}
else {
const size_type old_size = size(); //获取之前数组的大小
const size_type len = old_size != 0 ? 2 * old_size : 1; //将当前数组的容量扩大为原来的两倍
iterator new_start = data_allocator::allocate(len); //重新分配新数组的起始迭代器
iterator new_finish = new_start;
__STL_TRY {
new_finish = uninitialized_copy(start, position, new_start); //将旧数组的值重新分配给当前的新数组
construct(new_finish, x); //将当前数组的水位的值设为x
++new_finish; //提升新数组的水位
new_finish = uninitialized_copy(position, finish, new_finish); //这语句感觉可有可无,因为它根本就不会执行,position即last,而finish也是last
}
# ifdef __STL_USE_EXCEPTIONS
catch(...) { //如果重新构造的新数组出现异常,则销毁当前新创建的数组,并释放内存空间
destroy(new_start, new_finish);
data_allocator::deallocate(new_start, len);
throw;
}
# endif /* __STL_USE_EXCEPTIONS */
destroy(begin(), end()); //将旧数组的空间释放掉
deallocate();
start = new_start; //new_start记录新数组的起始位置
finish = new_finish; //重新设置当前水位的指针
end_of_storage = new_start + len; //设置新数组的容量
}
}
二、size和capacity
size():返回vector中元素的个数,这是vector中保存的实际对象的数量,不一定等于它的存储容量。
capacity():返回为当前vector分配的存储空间的大小(以其可以容纳的元素个数来表示)即:返回当前vector分配的存储空间可以存储的元素的个数。此容量不一定等于vector的size。它可以相等或者更大,多余的空间可以容纳增长,而无需在每次插入时重新分配。请注意,此容量并不代表vector的大小受到限制。当此容量用完并且需要更多容量时,容器会自动扩展它(重新分配存储空间)。可以通过vector::reserve显示更改vector的容量。
三、resize和reserve
1.resize
void resize (size_type n, value_type val = value_type());
调整容器size,使其包含n个元素。
- 如果n小于当前容器的size,则将内容减少到其前n个元素,并删除超出范围的元素(并销毁它们)。
- 如果n大于当前容器的size,则通过在末尾插入所需数量的元素来扩展内容,使得size = n.如果指定了val,则将新元素初始化为val,否则,按类型默认初始化。
- 如果n也大于当前容器的capacity,则将自动重新分配新的存储空间。
注意,此函数通过插入或擦除容器中的元素来更改容器的实际内容。
参数val:如果n大于当前容器的size,则将其内容复制到添加的匀速的对象,如果未指定,使用默认构造函数。
如果容器缩小,则所有未删除的元素的迭代器,指针和引用在调整大小后仍然有效,并且引用的是与调用之前引用的元素相同的元素。
如果容器扩展,则结束迭代器无效,并且如果必须重新分配存储,则与此容器相关的所有迭代器,指针和引用也将无效。
int main()
{
vector<int> vec;
for (int i = 0 ; i< 5; i++)
{
vec.push_back(1);
}
cout <<"capacity:" << vec.capacity() << ", size:" << vec.size() << endl;
vec.resize(7);
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;
vec.push_back(3);
return 0;
}
总结:
resize(n)改变size()的大小:
1)若n>原size,则vector的size为n,若n<=capacity,vector的capacity不变,若n>capacity,vector的capacity也发生改变。
2)若n<原size,vector的size变为n,capacity不变。
2.reserve
void reserve (size_type n);
更改vector的容量(capacity),使得其容量能至少包含n个元素。
如果n大于当前capacity,该函数使得容器重新分配内存,将其容量(capacity)增加到n(或更大).在其他素有情况下,函数调用不会导致重新分配,vector也不会受到影响。
这个函数对vector的size没有影响,并且不能改变它的元素。
int main()
{
vector<int> vec;
for (int i = 0 ; i< 5; i++)
{
vec.push_back(1);
}
cout <<"capacity:" << vec.capacity() << ", size:" << vec.size() << endl;
vec.reserve(7);//[1,1,1,1,1]
//vec.resize(7);//[1,1,1,1,1,0,0]
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;
vec.push_back(3);
return 0;
}
总结:
reserve(n),只是改变capacity的大小(当n<原来capacity时,size和capacity不会变)
resize和reserve总结:
resize(n):当n>capacity时,该函数既修改了capacity也修改了size,并且创建了对象。
reserve(n):当n>capacity时,该函数只改变了capacity的大小没有修改size,但是不会创建新的对象,需要通过push_back()或者insert()等创建对象。
四、关于reserve的应用
用法:
如果vecStockInfo没有提前reserve的话,push_back的时候就会导致多次的新建拷贝销毁,影响效率。此处用resize也可以。
五、内存释放
vector有一个特点:内存空间只会增长不会减少。vector有两个函数,一个是capacity(),返回对象缓冲区(vector维护的内存空间)实际申请的空间大小,另一个size(),返回当前对象缓冲区存储数据的个数。对于vector来说,capacity是永远大于等于size的,当capacity和size相等时,vector就会扩容,capacity变大。
在调用push_back时,若当前容量已经不能够放入新的元素(capacity=size),那么vector会重新申请一块内存,把之前的内存里的元素拷贝到新的内存当中,然后把push_back的元素拷贝到新的内存中,最后要析构原有的vector并释放原有的内存。所以说这个过程的效率是极低的,为了避免频繁的分配内存,C++每次申请内存都会成倍的增长,例如之前是4,那么重新申请后就是8,以此类推。当然不一定是成倍增长,比如在我的编译器环境下实测是0.5倍增长,之前是4,重新申请后就是6。
就像前面所说的,vector的内存空间是只增加不减少的,我们常用的操作clear()和erase(),实际上只是减少了size,清除了数据,并不会减少capacity,所以内存空间没有减少。(注意:其他容器(map/set/list/deque)都只需要 clear() 就能释放内存。只有含 reserve()/capacity() 成员函数的容器才需要用 swap 来释放空间,而 C++ 里只有 vector 和 string 这两个符合条件。)
int main()
{
vector<int> vec;
for (int i = 0 ; i< 5; i++)
{
vec.push_back(1);
}
cout <<"capacity:" << vec.capacity() << ", size:" << vec.size() << endl;
for (auto it = vec.begin(); it != vec.end();)
{
it = vec.erase(it);
}
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;
return 0;
}
swap()
那么如何释放内存空间呢,正确的做法是swap()操作。
1)语法:
//std::vector::swap
//vector容器,swap函数原型
void swap(vector& x);
swap()交换两个vector
源代码:
/**
* @brief Swaps data with another %vector.
* @param x A %vector of the same element and allocator types.
*
* This exchanges the elements between two vectors in constant time.
* (Three pointers, so it should be quite fast.)
* Note that the global std::swap() function is specialized such that
* std::swap(v1,v2) will feed to this function.
*/
void swap(vector& __x)
{
std::swap(this->_M_impl._M_start, __x._M_impl._M_start);
std::swap(this->_M_impl._M_finish, __x._M_impl._M_finish);
std::swap(this->_M_impl._M_end_of_storage, __x._M_impl._M_end_of_storage);
}
仅仅是交换了指向的首尾指针和容量指针
2)全部清空可以考虑:vector<int>().swap(vec);
int main()
{
vector<int> vec;
for (int i = 0 ; i< 5; i++)
{
vec.push_back(1);
}
cout <<"capacity:" << vec.capacity() << ", size:" << vec.size() << endl;//capacity:6, size:5
for (auto it = vec.begin(); it != vec.end();)
{
it = vec.erase(it);
}
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;//capacity:6, size:0
vector<int>().swap(vec);
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;//capacity:0, size:0
return 0;
}
swap交换技巧实现内存释放思想:vector()使用vector的默认构造函数建立临时vector对象,再在该临时对象上调用swap成员,swap调用之后对象vec占用的空间就等于一个默认构造的对象的大小,临时对象就具有原来对象v的大小,而该临时对象随即就会被析构,从而其占用的空间也被释放。
3)vector<int> tmpVec(vec);这样创建出来的对象是没有空闲空间的。
int main()
{
vector<int> vec;
for (int i = 0 ; i< 7; i++)
{
vec.push_back(i);
}
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;//capacity:9, size:7
for (auto it = vec.begin(); it != vec.end();)
{
if (*it > 2)
{
it = vec.erase(it);
}
else
{
it++;
}
}
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;//capacity:9, size:3
vector<int> tmpVec(vec);
cout << "tmp vec capacity:" << tmpVec.capacity() << ", tmp vec size:" << tmpVec.size() << endl;//capacity:3, size:3
return 0;
}
4)vector<int>(vec).swap(vec);释放过剩的容量
由于在vector中,其内存占用的空间是只增不减的,比如说首先分配了10,000个字节,然后erase掉后面9,999个,则虽然有效元素只有一个,但是内存占用仍为10,000个。所有内存空间在vector析构时回收。一般,我们都会通过vector中成员函数clear进行一些清除操作,但它清除的是所有的元素,使vector的大小减少至0,却不能减小vector占用的内存。要避免vector持有它不再需要的内存,这就需要一种方法来使得它从曾经的容量减少至它现在需要的容量,这样减少容量的方法被称为“收缩到合适(shrink to fit)”。(节选自《Effective STL》)如果做到“收缩到合适”呢,这就需要用“swap”函数,即通过如下代码进行释放过剩的容量:
int main()
{
vector<int> vec;
for (int i = 0 ; i< 7; i++)
{
vec.push_back(i);
}
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;//capacity:9, size:7
for (auto it = vec.begin(); it != vec.end();)
{
if (*it > 2)
{
it = vec.erase(it);
}
else
{
it++;
}
}
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;//capacity:9, size:3
vector<int>(vec).swap(vec);
cout << "capacity:" << vec.capacity() << ", size:" << vec.size() << endl;//capacity:3, size:3
return 0;
}
vector<int>(vec).swap(vec);
- 知识点1:vector<int> tmpVec(vec);这样创建出来的对象是没有空闲空间的,即因为此时vec的size是3,所以构造出来的vector的size和capacity都是3
- 知识点2:vector<int>(vec),创立的是一个匿名临时对象tmpVec,swap后vec占用的空间就是该临时对象占用的空间3,而临时对象就具有原来对象vec的大小(capacity)9
- 知识点3:而该临时对象在这句话结束后随即就会被析构,从而其占用的空间也会被释放。
参考:
https://blog.csdn.net/Czyaun/article/details/104600193/ vector容器的动态分配空间
https://blog.csdn.net/ubunfans/article/details/8447833 关于vector的resize()的理解
https://blog.csdn.net/xnmc2014/article/details/86748138 vector内存分配
https://zouzhongliang.com/index.php/2019/07/23/vectorxiangliangswaphanshuyunyongshili/ vector向量swap()函数运用实例
https://blog.csdn.net/sunmenggmail/article/details/8605538 vector 释放内存 swap
https://zhuanlan.zhihu.com/p/77932020 C++ vector 使用注意事项
https://blog.csdn.net/qq_41929943/article/details/103190891 C++ vector容器的swap方法(容器互换)