vector中的push_back(涉及到动态分配),resize,reserve,swap方法解析

目录

一、vector的push_back解析

二、size和capacity

三、resize和reserve

1.resize

2.reserve

四、关于reserve的应用

五、内存释放

swap()

1)语法:

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方法(容器互换)

 

 

  • 16
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值