C++基础篇 — vector的resize函数和reserve函数

对于C++的vector容器模板类,存在size和capacity这样两个概念,可以分别通过vector的size()和capacity()方法获得该vector当前的size和capacity值。相应的,vector提供了两个方法来分别对size和capacity进行操作,它们就是resize方法和reserve方法。首先,对于size和capacity,这是两个比较容易混淆的概念。

大家好,又见面了,我是你们的朋友全栈君。

对于C++的vector容器模板类,存在size和capacity这样两个概念,可以分别通过vector的size()和capacity()方法获得该vector当前的size和capacity值。相应的,vector提供了两个方法来分别对size和capacity进行操作,它们就是resize方法和reserve方法。

首先,对于size和capacity,这是两个比较容易混淆的概念。都说要抱着问题来学习,才能做到事半功倍。那么,这里便提出三个问题:什么是vector的大小(即size)?什么是vector的容量(即capacity)?这两个概念的区别在哪里?

对于抽象的问题,只要我们把它们同我们的生活实际相结合,将问题具象化,自然就会很好的理解。

就拿我们的办公室举例,假设,我们部门的办公地点位于公司大楼的六楼。在我们的办公室里面,放置了100套办公桌椅(工位),公司说按照一个萝卜一个坑来算,你们部门最多只能招这么多人,那么,这时我们可以说,我们部门的容量(即capacity)就是100人,如果我们部门是公司刚成立的部门,正处于发展壮大的阶段,目前只有40为员工,也就是说,办公室里只坐了40个人,另外60个工位是空着的,那么,我们可以说,我们部门当前的大小(即size)是40人。这实际上就是size和capacity的区别。类比到vector,size和capacity的概念自然就很清楚了。

cplusplus.com中对capacity是这样定义的:

This capacity is not necessarily equal to the vector size. It can be equal or greater, with the extra space allowing to accommodate for growth without the need to reallocate on each insertion.

一个allowing道出了真谛!这里还要区分两个概念,就是:为vector分配的存储空间和vector的大小是两个不同的概念。为vector分配的存储空间,实际上就是capacity,指的是当前vector最多能使用的存储空间,是大于等于vector的大小的,当vector实际需要使用的存储空间大于当前分配给它的存储空间时,需要重新为其分配存储空间。

cplusplus.com中对size的定义是:

This is the number of actual objects held in the vector, which is not necessarily equal to its storage capacity.

实际上就是vector中当前实际存储的元素个数。


弄清楚了size和capacity这两个概念之后,对于resize和reserve两个方法就很好理解了。

cplusplus.com中对reserve的定义是:

Request a change in capacity

Requests that the 
vector capacity be at least enough to contain 
n elements.

If 
n is greater than the current 
vector capacity, the function causes the container to reallocate its storage increasing its
capacity to 
n (or greater).

In all other cases, the function call does not cause a reallocation and the 
vector capacity is not affected.

This function has no effect on the 
vector size and cannot alter its elements.

从上面的说明中,可以得到以下信息:

1、reserve方法被用来重新分配vector的容量大小;

2、只有当所申请的容量大小n大于vector的当前容量时,才会重新为vector分配存储空间;

3、reserve方法对于vector的大小(即size)没有任何影响;

具体通过下面的例子验证

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> vect;

	vect.reserve(5);    // 调用reserve方法为vect分配容量(即存储空间)

	vect.push_back(1);
	vect.push_back(2);
	vect.push_back(3);
	vect.push_back(4);  // 插入4个元素

	cout << vect.size() << endl;      // vect的实际大小(即包含多少元素)
	cout << vect.capacity() << endl;     // vect的容量大小

	return 0;
}

结果为

C++基础篇 -- vector的resize函数和reserve函数


从结果中,就可以很清楚的看到capacity和size的区别。

下面看一个综合的例子,可以从中获得很多信息:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> vect;

	vect.reserve(5);

	vect.push_back(1);
	vect.push_back(2);
	vect.push_back(3);
	vect.push_back(4);

	cout << vect.size() << endl;
	cout << vect.capacity() << endl;

	vect.push_back(5);                               
	vect.push_back(6);                       // 插入两个元素,此时vect的大小大于之前分配的容量5
	
	cout << "size1 = " << vect.size() << endl; 
	cout << "capacity1 = " << vect.capacity() << endl;

	vect.push_back(7);
	vect.push_back(8);                       // 在插入两个元素,和上面的结果进行对比,会有意外收获
	cout << "size1_1 = " << vect.size() << endl; 
	cout << "capacity1_1 = " << vect.capacity() << endl;

	vect.reserve(3);                        // 当程序执行到此处时,vect的容量大小一定是大于3的

	cout << "size2 = " << vect.size() << endl;
	cout << "capacity2 = " << vect.capacity() << endl;

	vect.reserve(12);

	cout << "size3 = " << vect.size() << endl;
	cout << "capacity3 = " << vect.capacity() << endl;

	return 0;
}

执行结果为:

C++基础篇 -- vector的resize函数和reserve函数


对这一执行结果,一点点进行分析:

1、首先,看结果size1和capacity1,在打印这两个结果前,程序向vect中插入了两个元素,之前,vect中存在4个元素且容量为5,按照我之前的设想,如果我采用push_back向vect中插入元素时,当元素数量大小capacity时,vect的capacity会随着size变大而变大,但应该是和size相等。但此处,vect的大小为6,但是容量却是7,且这个7,来的很是突然,我往哪个方面靠都靠不上啊。好吧,先把这个疑问姑且放下,现在我们猜想,是不是push_back中针对这种情况会有处理,始终保持vect的capacity比size至少大1,带着这个猜想继续向下看,我又向vect中插入了两个元素,此时,vect的大小为8,若是我们刚才的猜想是正确的话,则此时,vect的capacity应该增大为9了,但是此时结果size1_1和capacity1_1却给了我当头一棒,这个10又是怎么回事?学习编程永远记住一件事情,所有问题的答案,都能从代码中找到。于是,我顺着vector的push_back源码开始找下去,就有了下面这段追踪代码

   ...
void push_back(_Ty&& _Val)
   ...
    if (this->_Mylast == this->_Myend)
	_Reserve(1);
   ...

void _Reserve(size_type _Count)
{	// ensure room for _Count new elements, grow exponentially
	size_type _Size = size();
	if (max_size() - _Count < _Size)
		_Xlen();
	else if ((_Size += _Count) <= capacity())
		;
	else
		reserve(_Grow_to(_Size));
}

size_type _Grow_to(size_type _Count) const
{	// grow by 50% or at least to _Count
	size_type _Capacity = capacity();

	_Capacity = max_size() - _Capacity / 2 < _Capacity
		? 0 : _Capacity + _Capacity / 2;	// try to grow by 50%
	if (_Capacity < _Count)
		_Capacity = _Count;
	return (_Capacity);
}

由这段官方实现代码,终于找到了答案,原来在使用push_back向vect中插入元素时,如果当前元素数量大于vector的capacity时,会重新为vector分配存储空间,而分配的原则就是:

                    原capacity + 原capacity / 2

这样,就解释了上面的7和10两个结果,最初,vect的容量大小为5,在第一次插入两个元素后,vector中的元素数量大于5了,所以此时,会重新为vect分配容量,分配大小为5 + 5 / 2 = 7,而同样的,第二次重新分配vect的容量是7 + 7 / 2 = 10,这就合理的解决了刚才的疑问。


2、当vect的容量大小为10时,再调用reserve方法,重新为其设置容量为3时,不会进行任何操作,可以看到,vect的容量大小还是10;

3、当为vect设置容量大小为12时,可以看到,成功的改变了vect的容量大小;

4、不论哪次调用reserve方法,vect的size大小在调用前后,始终没有被改变过。


到此,是我对reserve方法的一些思考和验证。


cplusplus.com中对resize的定义是:

Resizes the container so that it contains n elements.

If n is smaller than the current container size, the content is reduced to its first n elements, removing those beyond (and destroying them).

If n is greater than the current container size, the content is expanded by inserting at the end as many elements as needed to reach a size of n. If val is specified, the new elements are initialized as copies of val, otherwise, they are value-initialized.

If n is also greater than the current container capacity, an automatic reallocation of the allocated storage space takes place.

Notice that this function changes the actual content of the container by inserting or erasing elements from it.

从上述说明中,可以得到下面的信息:

1、resize方法被用来改变vector的大小,即vector中元素的数量,我们可以说,resize方法改变了容器的大小,且创建了容器中的对象;

2、如果resize中所指定的n小于vector中当前的元素数量,则会删除vector中多于n的元素,使vector得大小变为n;

3、如果所指定的n大于vector中当前的元素数量,则会在vector当前的尾部插入适量的元素,使得vector的大小变为n,在这里,如果为resize方法指定了第二个参数,则会把后插入的元素值初始化为该指定值,如果没有为resize指定第二个参数,则会把新插入的元素初始化为默认的初始值;

4、如果resize所指定的n不仅大于vector中当前的元素数量,还大于vector当前的capacity容量值时,则会自动为vector重新分配存储空间;


还是通过代码来说话:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> vect;
	int i = 0;

	vect.reserve(10);

	vect.push_back(1);
	vect.push_back(2);
	vect.push_back(3);
	vect.push_back(4);
	vect.push_back(5);
	vect.push_back(6);
	vect.push_back(7);
	vect.push_back(8);                               // 此时vect的size大小为8

	cout << vect.size() << endl;
	cout << vect.capacity() << endl;

	vect.resize(6);                                   // 此处设置vect的大小比当前vect中元素数量小,且没有指定初始化值

	cout << "size1 = " << vect.size() << endl;
	cout << "capacity1 = " << vect.capacity() << endl;

	for (i = 0 ; i < vect.size(); i++)
	{
		cout << vect[i] << endl;
	}

	vect.resize(4, 10);                              // 此处设置vect的大小为4,比前面的6小,且指定了初始化值,看是否会改变前四个元素的值

	cout << "size1_1 = " << vect.size() << endl;
	cout << "capacity1_1 = " << vect.capacity() << endl;

	for (i = 0 ; i < vect.size(); i++)
	{
		cout << vect[i] << endl;
	}

	vect.resize(8, 7);                         // 此处设置vect的大小为8,大于当前vect的大小4,但是小于vect的当前容量10,指定初始化值为7

	cout << "size2= " << vect.size() << endl;

	cout << "capacity2 = " << vect.capacity() << endl;

	for (int i = 0 ; i < vect.size(); i++)
	{
		cout << vect[i] << endl;
	}

	vect.resize(10);                           // 此处设置vect的大小为10,大于当前vect的大小8,但是等于vect的当前容量10,没有指定初始化值,采用默认值

	cout << "size3 = " << vect.size() << endl;
	cout << "capacity3 = " << vect.capacity() << endl;

	for (int i = 0; i < vect.size(); i++)
	{
		cout << vect[i] << endl;
	}

	vect.resize(12, 77);                 // 此处设置vect的大小为10,不仅大于当前vect的大小10,还大于vect的当前容量10,会为vect重新分配存储空间

	cout << "size4 = " << vect.size() << endl;
	cout << "capacity4 = " << vect.capacity() << endl;

	for (int i = 0; i < vect.size(); i++)
	{
		cout << vect[i] << endl;
	}

	return 0;
}

上述代码的执行结果为:

C++基础篇 -- vector的resize函数和reserve函数


由上面的执行结果,可以得到下面几个结论:

1、使用resize方法时,改变了vector中元素的个数,即vector的大小,但不会改变vector的容量大小;

2、验证了“如果resize中所指定的n小于vector中当前的元素数量,则会删除vector中多于n的元素,使vector得大小变为n;”,由size1、capacity1、size1_1、capacity1_1,可知,如果resize指定的大小n小于vector当前的大小时,会减小vector的大小,但无论resize中是否指定初始化值,都不会影响vector中原本已经存在的元素值;

3、当resize中所指定的n大于vector当前的大小,但是小于vector当前的容量大小时,会在vector后面插入适量的元素,使得vector的大小满足n,如果指定了初始值,则会把新插入的元素初始化为指定的初始值,如果没有指定初始值,则将会把新插入的元素初始化为默认初始值,即0;(由size2、capacity2、size3、capacity3得出)

4、当resize中所指定的n大于vector当前的大小,并且大于vector当前的容量大小时,会为vector重新分配存储空间,由size4和capacity4可以看出来,其中,对于capacity = 15的原因上面已经解释过,这里不再赘述。


这里再补充一点:

reserve被用来为vector设置容量大小,但是并没有创建vector中的元素对象,必须保证vector中有通过push_back或者insert等方法插入的元素后,才能访问vector中的元素;而resize被用来设置vector的大小,即元素个数,同时会创建元素对象,因此可以通过重载标识符[]来访问vector中resize所指定的大小范围内的元素,若是采用push_back或者insert方法向vector中插入元素的话,则只会在vector的尾部插入新的元素,增加vector的元素个数,扩大vector的大小


通过下述代码进行验证:

1、

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> vect;
	int i = 0;

	vect.reserve(10);

	cout << vect.size() << endl;
	cout << vect.capacity() << endl;

	vect[0] = 3;

	return 0;
}

首先,在使用reserve为vect设置容量大小之后,通过[]来访问 vect中的第一个元素,此时,在编译链接过程中,不会有任何问题,但是在执行的过程中会报出:

C++基础篇 -- vector的resize函数和reserve函数

该异常信息为:vector subscript out of range,即访问vect越界了,由此可以看出,在使用reserve时并没有为vector创建任何元素对象

2、

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> vect;
	int i = 0;

	vect.resize(6);

	cout << "size1 = " << vect.size() << endl;
	cout << "capacity1 = " << vect.capacity() << endl;
    
	vect[5] = 17;

	for (i = 0 ; i < vect.size(); i++)
	{
		cout << vect[i] << endl;
	}

	vect.push_back(777);

	cout << "size1 = " << vect.size() << endl;
	cout << "capacity1 = " << vect.capacity() << endl;

	for (i = 0 ; i < vect.size(); i++)
	{
		cout << vect[i] << endl;
	}

	return 0;
}

上述代码的执行结果为:

C++基础篇 -- vector的resize函数和reserve函数

由上述执行结果可以看出,在使用resize为vector指定大小之后,会创建元素对象,可以通过[]标识符来对元素对象进行访问,同时若是采用push_back或者insert等方法来插入元素时,实际上会在vector尾部插入新的元素,从上面的size1值的改变可以证实。


本文为VampirEM原创博文,如需转载,请注明出处!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/126346.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • sklearn.metrics.auc_auc值计算公式

    sklearn.metrics.auc_auc值计算公式fromsklearnimportcross_validation,metricsfromsklearnimportsvmtrain_data,train_target=load(filename)#自定义加载数据函数,返回的是训练数据的数据项和标签项train_x,test_x,train_y,test_y=cross_validation.train_test_split

  • 大数据面试题——HBase面试题总结

    大数据面试题——HBase面试题总结1、HBase的特点是什么?1)大:一个表可以有数十亿行,上百万列;2)无模式:每行都有一个可排序的主键和任意多的列,列可以根据需要动态的增加,同一张表中不同的行可以有截然不同的列;3)面向列:面向列(族)的存储和权限控制,列(族)独立检索;4)稀疏:空(null)列并不占用存储空间,表可以设计的非常稀疏;5)数据多版本:每个单元中的数据可以有多个版本,默认情况下版本号自动分配,是单元格插入时的时间戳;6)数据类型单一:Hbase中的数据都是字符串,没有类型。2…

  • java二维数组输入_java 二维数组的输入输出问题[通俗易懂]

    java二维数组输入_java 二维数组的输入输出问题[通俗易懂]java二维数组的输入输出问题有一个二维数组里面包含了很大的数字。我要把它通过输出流存储在.dat文件中然后在另一个程序中把这些信息读取出来,并且存储在另外一个二维数组中。请问该怎么办?这个数组如下:intmapl[][]={{0,0,0,0,148,149,149,149,149,149,149,149,149,149,178,0,0,0,0,0,…

  • linux端口转发技术(单端口分发)

    端口转发映射的程序叫rinetd,下载地址,直接manke编译安装即可。12345678910111213141516[root@PortForward02 src]# wget http://www.boutell.com/r

  • 声明方法java实际开发中泛型使用需要注意的一些问题

    声明方法java实际开发中泛型使用需要注意的一些问题

  • Linux抓包命令_怎么使用wireshark抓包

    Linux抓包命令_怎么使用wireshark抓包Wireshark是一款图形化的抓包软件,在LInux和Windows下都可以下载。用命令安装wireshark相关软件包命令:查看安装wireshark产生了哪些文件直接通过命令打开或者是图形化页面点开就好点击InterfaceList,就可以看到接口列表,选择需要抓哪个网卡的包这里我选择ens33网卡,点击start开始抓包我们ping我们的主机地址,看看抓包情况ping工具使用的就是协议,ICMP是IP协议的附属协议。IP层用它来与其他主机或路由器交换错误报文和其他重要信息。它主要是

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号