72.STL中的vector
72.STL中的vector
1.vector概述
vector是最常用的容器之一,功能十分强大,可以储存、管理各种类型的数据。在很多情况下可以用来代替功能比较局限的普通数组,因为我们知道,普通数组只能实现一对一的映射而不能实现一对多的映射,vector就是专门为了解决这个问题而诞生的。vector也可以称为动态数组,因为其大小是根据实时更新而变化的,正因为如此vector显得更加灵活易用。
vector的数据安排以及操作方式,与array非常相似,两者的唯一差别在于空间的运用的灵活性。Array是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎得由自己来,首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间。Vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此vector的运用对于内存的合理利用与运用的灵活性有很大的帮助,我们再也不必害怕空间不足而一开始就要求一个大块头的array了。
Vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率,一旦vector旧空间满了,如果客户每新增一个元素,vector内部只是扩充一个元素的空间,实为不智,因为所谓的扩充空间(不论多大),一如刚所说,是”配置新空间-数据移动-释放旧空间”的大工程,时间成本很高,应该加入某种未雨绸缪的考虑,稍后我们便可以看到vector的空间配置策略。
C++语言既有类模板(class template),也有函数模板,其中vector是个类模板。
模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的份说明。编译器根据模板创建类或函数的过程称为实例化(instantiation),当使用模板时,需要指出编译器应把类或函数实例化成何种类型。
NOTE:vector是模板而非类型,由vector,生成的类型必须包含vector中元素的类型,例如vector<int>。
vector能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的vector。除此之外,其他大多数(非引用)内置类型和类类型都可以构成vector对象,甚至组成vector的元素也可以是vector。
2.vector的定义和初始化
使用vector需要包含vector头文件如下:
#include<vector>
using std::vector;
和任何种类类型一样,vector模板控制着定义和初始化向量的方法。表3.4列出了vector对象的常用方法
表3.4: 初始化vector对象的方法 | |
vector<T> v1 | v1是一个空vector,它潜在的元素是T类型的,执行默认初始化 |
vector<T> v2(v1) | v2中包含有v1所有元素的副本 |
vector<T> v2 = v1 | 等价于v2(v1), v2中包含有v1所有元素的副本 |
vector<T> v3(n, val) | v3包含了n个重复的元素,每个元素的值都是val |
vector<T> v4(n) | v4包含了n个重复地执行了值初始化的对象 |
vector<T> v5{a,b,c...} | v5包含了初始值个数的元素,每个元素被赋予相应的初始值 |
vector<T> v5={a,b,c...} | 等价于v5{a,b,c...} |
例子:
vector<int> v1;//声明一个int型向量v1,默认初始化为0
vector<int> v2(v1);//声明并用向量v1初始化向量v2,v2是包含有v1所有元素的副本
vector<int> v2 = v1;//等价于v2(v1),声明并用向量v1初始化向量v2,v2是包含有v1所有元素的副本
vector<int> v3(10, 1);//向量v3包含了10个重复的元素,每个元素都是1
vector<int> v4(10);//向量v1包含了10个重复地执行了值初始化的对象
vector<int> v5{a, b, c,..};//向量v1包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<int> v5={ a, b, c,.. };//等价于v5{a, b, c,..};
vector<int> v6(a.begin(), a.begin() + 3);//将v6向量中从第0个到第2个(共3个)作为向量b的初始值
vector<string> v7 = { "hi","my","name","is","lee" };
int arr[] = { 0,1,2,3,4,5,6 };
vector<int>v8 = { 0,1,2,3,4 };//列表初始化
vector<int>v9(arr, arr + 3);//将arr数组的前是三个元素作为向量v9的初始值,区间为[arr,arr+3)
vector<int>v10(&arr[1], &arr[4]);//将arr[1]~arr[4]之间的元素作为向量v8的初始值
(1)值初始化
通常情况下,可以只提供vector对象容纳的元素数量而不用略去初始值。此时库会创建一个值初始化的(value-initialized)元素初值,并把它赋给容器中的所有元素。这个初值由vector对象中元素的类型决定。
如果vector对象的元素是内置类型,比如int,则元素初始值自动设为0。如果元素是某种类类型,比如string,则元素由类默认初始化 :
vector<int> ivec(10);//10个元素,每个都初始化为0
vector<string> svec(10);//10个元素,每个都是空string
对这种初始化的方式有两个特殊限制:其一,有些类要求必须明确地提供初始值,如果vector对象中元素的类型不支持默认初始化,我们就必须提供初始的元素值。对这种类型的对象来说,只提供元素的数量而不设定初始值无法完成初始化工作。
其二,如果只提供了元素的数量而没有设定初始值,只能使用直接初始化:
vector<int> vi= 10;//错误:必须使用直接初始化的形式指定向量大小
这里的10是用来说明如何初始化vector对象的,我们用它的本意是想创建含有10个值初始化了的元素的vector对象,而非把数字10 “拷贝” 到vector中。因此,此时不宜使用拷贝初始化。
3.vector迭代器
迭代器 | 功能 |
---|---|
begin() | 返回指向向量第一个元素的迭代器 |
end() | 返回指向向量最后一个元素之后位置的迭代器 |
rbegin() | 返回指向向量最后一个元素的反向迭代器 |
rend() | 返回指向向量第一个元素之前位置的反向迭代器 |
cbegin() | 返回指向向量第一个元素的迭代器,不能用于修改元素 |
cend() | 返回指向向量最后一个元素之后位置的迭代器,不能用于修改元素 |
crbegin() | 返回指向向量最后一个元素的反向迭代器,不能用于修改元素 |
crend() | 返回指向向量第一个元素之前位置的反向迭代器,不能用于修改元素 |
4.向vector对象中添加元素
(1)a.assign(b.begin(), b.begin()+3); //b为向量,将b的0~2个元素构成的向量赋给a
(2)a.assign(4,2); //是a只含4个元素,且每个元素为2
(3)a.back(); //返回a的最后一个元素
(4)a.front(); //返回a的第一个元素
(5)a[i]; //返回a的第i个元素,当且仅当a[i]存在2013-12-07
(6)a.clear(); //清空a中的元素
(7)a.empty(); //判断a是否为空,空则返回ture,不空则返回false
(8)a.pop_back(); //删除a向量的最后一个元素
(9)a.erase(a.begin()+1,a.begin()+3); //删除a中第1个(从第0个算起)到第2个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+ 3(不包括它)
(10)a.push_back(5); //在a的最后一个向量后插入一个元素,其值为5
(11)a.insert(a.begin()+1,5); //在a的第1个元素(从第0个算起)的位置插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4
(12)a.insert(a.begin()+1,3,5); //在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
(13)a.insert(a.begin()+1,b+3,b+6); //b为数组,在a的第1个元素(从第0个算起)的位置插入b的第3个元素到第5个元素(不包括b+6),如b为1,2,3,4,5,9,8 ,插入元素后为1,4,5,9,2,3,4,5,9,8
(14)a.size(); //返回a中元素的个数;
(15)a.capacity(); //返回a在内存中总共可以容纳的元素个数
(16)a.resize(10); //将a的现有元素个数调至10个,多则删,少则补,其值随机
(17)a.resize(10,2); //将a的现有元素个数调至10个,多则删,少则补,其值为2
(18)a.reserve(100); //将a的容量(capacity)扩充至100,也就是说现在测试a.capacity();的时候返回值是100.这种操作只有在需要给a添加大量数据的时候才 显得有意义,因为这将避免内存多次容量扩充操作(当a的容量不足时电脑会自动扩容,当然这必然降低性能)
(19)a.swap(b); //b为向量,将a中的元素和b中的元素进行整体性交换
(20)a==b; //b为向量,向量的比较操作还有!=,>=,<=,>,<
对vector对象来说,直接初始化的方式适用于三种情况:初始值已知且数量较少、初始值是另一个vector对象的副本、所有元素的初始值都一样。然而更常见的情况是:创建一个vector对象时并不清楚实际所需的元素个数,元素的值也经常无法确定。还有 些时候即使元素的初值已知,但如果这些值总量较大而各不相同,那么在创建vector对象的时候执行初始化操作也会显得过于烦琐。
举个例子,如果想创建一个vector对象令其包含从0到9共10个元素,使用列表初始化的方法很容易做到这一点:但如果vector对象包含的元素是从0到99或者从0 到999呢?这时通过列表初始化把所有元素都一一罗列出来就不太合适了。对于此例来说,更好的处理方法是先创建一个空vector,然后在运行时再利用vector的成员函数 push_back向其中添加元素。push_back负责把一个值当成vector对象的尾元素“压到(push)”vector对象的“尾端(back) ”。例如:
vector<int> vec;
for (int i = 0; i != 100; i++)
{
vec.push_back(i);
}
// 循环结束后vec中100个元素,值从0到99
同样的,如果直到运行时才能知道vector对象中元素的确切个数,也应该使用刚刚这种方法创建 vector对象并为其赋值。例如,有时需要实时读入数据然后将其赋予vector对象:
//从标准输入中读取单词,将其作为vector对象的元素存储
string word;
vector<string> text; //空vector对象
while (cin >> word)
{
text.push_back(word);//把word添加到text后面
}
和之前的例子一样,本例也是先创建一个空vector,之后依次读入未知数量的值并保存到test中。
关键概念:vector对象能高效增长
C++标准要求vector应该能在运行时高效快速地添加元素。因此既然vector对 象能高效地增长,那么在定义vector对象的时候设定其大小也就没什么必要了,事实上如果这么做性能可能更差。只有一种例外情况,就是所有 (all) 元素的值都一样。一旦元素的值有所不同,更有效的办法是先定义一个空的vector对象,再在运行时向其中添加具体值。此外,允许我们进一步提升动态添加元素的性能。
开始的时候创建空的vector对象在运行时再动态添加元素,这一做法与C语言及其他大多数语言中内置数组类型的用法不同。特别是如果用惯了C或者Java,可以预计在创建vector对象时顺便指定其容量是最好的。然而事实上,通常的情况是恰恰相反。
(1)向vector对象添加元素蕴含的编程假定
由于能高效便捷地向vector对象中添加元素,很多编程工作被极大简化了。然而,这种简便性也伴随着一些对编写程序更高的要求:其中一条就是必须要确保所写的循环正确无误,特别是在循环有可能改变vector对象容量的时候。
随着对vector的更多使用,我们还会逐渐了解到其他一些隐含的要求,其中一条是现在就要指出的:如果循环体内部包含有向vector对象添加元素的语句,则不能使用范围for循环,因为在范围for语句中,预存了尾部位置相关的标记位置的值,增减元素会改变其值。
5.vector类常用的函数
5.1构造函数
vector():创建一个空vector
vector(int nSize):创建一个vector,元素个数为nSize
vector(int nSize,const T& t):创建一个vector,元素个数为nSize,且值均为t
vector(const vector&):复制构造函数
vector(begin,end):复制[begin,end)区间内另一个数组的元素到vector中
例1:
//#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
class A
{
//空类
};
int main(int argc, char* argv[])
{
//int型vector
vector<int> vecInt;
//float型vector
vector<float> vecFloat;
//自定义类型,保存类A的vector
vector<A> vecA;
//自定义类型,保存指向类A的指针的vector
vector<A*> vecPointA;
return 0;
}
例2:
// vectorsample.cpp : 定义控制台应用程序的入口点。
//
//#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
class A
{
//空类
};
int main(int argc, char* argv[])
{
//int型vector,包含3个元素
vector<int> vecIntA(3);
//int型vector,包含3个元素且每个元素都是9
vector<int> vecIntB(3, 9);
//复制vecIntB到vecIntC
vector<int> vecIntC(vecIntB);
int iArray[] = { 2,4,6 };
//创建vecIntD
vector<int> vecIntD(iArray, iArray + 3);
//打印vectorA,此处也可以用下面注释内的代码来输出vector中的数据
/*for(int i=0;i<vecIntA.size();i++)
{
cout<<vecIntA[i]<<" ";
}*/
cout << "vecIntA:" << endl;
for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//打印vecIntB
cout << "VecIntB:" << endl;
for (vector<int>::iterator it = vecIntB.begin(); it != vecIntB.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//打印vecIntC
cout << "VecIntB:" << endl;
for (vector<int>::iterator it = vecIntC.begin(); it != vecIntC.end(); it++)
{
cout << *it << " ";
}
cout << endl;
//打印vecIntD
cout << "vecIntD:" << endl;
for (vector<int>::iterator it = vecIntD.begin(); it != vecIntD.end(); it++)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
输出:
vecIntA:
0 0 0
VecIntB:
9 9 9
VecIntB:
9 9 9
vecIntD:
2 4 6
例3:
#include<iostream>
#include<vector>
using namespace std;
void printVector(vector<int>& v)
{ //利用迭代器打印 v
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
void text01()
{
vector<int> v1;
for (int i = 0; i < 5; ++i)
{
v1.push_back(i);//向v1末尾添加数据
}
vector<int> v2(v1.begin(), v1.end());
vector<int> v3(5, 5);
vector<int> v4(v3);
cout << "打印v2: ";
printVector(v2);
cout << "打印v3: ";
printVector(v3);
cout << "打印v4: ";
printVector(v4);
}
int main(int argc, char* argv[])
{
text01();
return 0;
}
输出:
打印v2: 0 1 2 3 4
打印v3: 5 5 5 5 5
打印v4: 5 5 5 5 5
5.2增加函数
void push_back(const T& x):向量尾部增加一个元素X
iterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
iterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
iterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据
例子:
// vectorsample.cpp : 定义控制台应用程序的入口点。
// vectorsample.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
int main(int argc, char* argv[])
{
//int型vector,包含3个元素
vector<int> vecIntA;
//插入1 2 3
vecIntA.push_back(1);
vecIntA.push_back(2);
vecIntA.push_back(3);
int nSize = vecIntA.size();
cout << "vecIntA:" << endl;
//打印vectorA,方法一:
for (int i = 0; i < nSize; i++)
{
cout << vecIntA[i] << " ";
}
cout << endl;
//打印vectorA,方法二:
for (int i = 0; i < nSize; i++)
{
cout << vecIntA.at(i) << " ";
}
cout << endl;
//打印vectorA,方法三:
for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
{
cout << *it << " ";
}
cout << endl;
return 0;
}
上述代码对一个整形向量类进行操作,先定义一个整形元素向量类,然后插入3个值,并用3种不同的方法输出,程序运行结果如下:
vecIntA:
1 2 3
1 2 3
1 2 3
例子2:
// vectorsample.cpp : 定义控制台应用程序的入口点。
//
//#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
class A
{
public:
int n;
public:
A(int n)
{
this->n = n;
}
};
int main(int argc, char* argv[])
{
//int型vector,包含3个元素
vector<A> vecClassA;
A a1(1);
A a2(2);
A a3(3);
//插入1 2 3
vecClassA.push_back(a1);
vecClassA.push_back(a2);
vecClassA.push_back(a3);
int nSize = vecClassA.size();
cout << "vecClassA:" << endl;
//打印vecClassA,方法一:
for (int i = 0; i < nSize; i++)
{
cout << vecClassA[i].n << " ";
}
cout << endl;
//打印vecClassA,方法二:
for (int i = 0; i < nSize; i++)
{
cout << vecClassA.at(i).n << " ";
}
cout << endl;
//打印vecClassA,方法三:
for (vector<A>::iterator it = vecClassA.begin(); it != vecClassA.end(); it++)
{
cout << (*it).n << " ";
}
cout << endl;
return 0;
}
上述代码通过定义元素为类的向量,通过插入3个初始化的类,并通过3种方法输出,运行结果如下:
vecClassA:
1 2 3
1 2 3
1 2 3
上述代码通过定义元素为类指针的向量,通过插入3个初始化的类指针,并通过3种方法输出指针指向的类,运行结果如下:
vecClassA:
1 2 3
1 2 3
1 2 3
5.3删除函数
iterator erase(iterator it) :删除向量中迭代器指向元素
iterator erase(iterator first, iterator last) : 删除向量中[first, last)中元素
void pop_back() :删除向量中最后一个元素
void clear() : 清空向量中所有元素
例1:
// vectorsample.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"
#include<iostream>
#include<vector>
using namespace std;
int main(int argc, char* argv[])
{
//int型vector,包含3个元素
vector<int> vecIntA;
//循环插入1 到10
for (int i = 1; i <= 10; i++)
{
vecIntA.push_back(i);
}
vecIntA.erase(vecIntA.begin() + 4);
cout << "删除第5个元素后的向量vecIntA:" << endl;
//打印vectorA
for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
{
cout << *it << "\t";
}
cout << endl;
//删除第2-5个元素
vecIntA.erase(vecIntA.begin() + 1, vecIntA.begin() + 5);
cout << "删除第2-5个元素后的vecIntA:" << endl;
//打印vectorA
for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
{
cout << *it << "\t";
}
cout << endl;
//删除最后一个元素
vecIntA.pop_back();
cout << "删除最后一个元素后的vecIntA:" << endl;
//打印vectorA
for (vector<int>::iterator it = vecIntA.begin(); it != vecIntA.end(); it++)
{
cout << *it << "\t";
}
cout << endl;
return 0;
}
程序运行结果如下:
删除第5个元素后的向量vecIntA:
1 2 3 4 6 7 8 9 10
删除第2-5个元素后的vecIntA:
1 7 8 9 10
删除最后一个元素后的vecIntA:
1 7 8 9
5.4遍历函数
reference at(int pos) :返回pos位置元素的引用
reference front() : 返回首元素的引用
reference back() : 返回尾元素的引用
iterator begin() : 返回向量头指针,指向第一个元素
iterator end() : 返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin() : 反向迭代器,指向最后一个元素
reverse_iterator rend() : 反向迭代器,指向第一个元素之前的位置
5.5判空函数
bool empty() const:判断向量是否为空,若为空,则向量中无元素
5.6大小函数
int size() const:返回向量中元素的个数
int capacity() const:返回当前向量所能容纳的最大元素值
int max_size() const:返回最大可允许的vector元素数量值
5.7其他函数
void swap(vector&):交换两个同类型向量的数据
void assign(int n,const T& x):设置向量中第n个元素的值为x
void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素
resize(num)重新指定队列的长度。(往往用来增加vector的长度,小->大 ok 大->小 没用!)
reserve()保留适当的容量。
front() 返回指向向量第一个元素的引用
back()返回指向向量最后一个元素的引用
1.assign函数介绍
函数原型:
void assign(const_iterator first, const_iterator last);
void assign(size_type n, const T& x = T());
void assign( std::initializer_list<T> ilist );//(C++11 起) (C++20 前)
constexpr void assign( std::initializer_list<T> ilist );//(C++20 起)
void assign(const_iterator first, const_iterator last);
功能:将区间[first,last)的元素赋值到当前的vector容器中,或者赋n个值为x的元素到vector容器中,这个容器会清除掉vector容器中以前的内容。
例1:
// vector assign
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
std::vector<double> first{ 1.9, 2.9, 3.9, 4.9, 5.9 }; /*初始化源数组*/
std::vector<double> second; /*声明空数组*/
std::vector<int> third;
std::vector<string> forth;
std::vector<double>::iterator it;
it = first.begin();
second.assign(it, first.end());
std::cout << "Size of second: " << int(second.size()) << '\n';
for (int i = 0; i < second.size(); i++)
cout << second[i] << " ";
cout << endl;
//结果:
//Size of second: 5
//1.9 2.9 3.9 4.9 5.9
second.assign(it + 1, first.end() - 1);
std::cout << "Size of second: " << int(second.size()) << '\n';
for (int i = 0; i < second.size(); i++)
cout << second[i] << " ";
cout << endl;
//Size of second: 3
//2.9 3.9 4.9
third.assign(it, first.end());
std::cout << "Size of third: " << int(third.size()) << '\n';
for (int i = 0; i < third.size(); i++)
cout << third[i] << " ";
cout << endl;
//Size of third: 5
//1 2 3 4 5
int myints[] = { 1776,7,4 };
third.assign(myints, myints + 3); /* assign with array*/
std::cout << "Size of third: " << int(third.size()) << '\n';
for (int i = 0; i < third.size(); i++)
cout << third[i] << " ";
cout << endl;
//Size of third: 3
//1776 7 4
//third.assign (myints,myints+4); /*error usage,有结果但是行为错误*/
//1776 7 4 787800
// third = first; /*error usage*/
// forth.assign(it,first.end()); /*error usage*/
return 0;
}
输出:
Size of second: 5
1.9 2.9 3.9 4.9 5.9
Size of second: 3
2.9 3.9 4.9
Size of third: 5
1 2 3 4 5
Size of third: 3
1776 7 4
void assign(size_type n, const T& x = T());
功能:将调用者中内容替换成n个x
例2:
// vector assign
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<double> first{ 1.9, 2.9, 3.9, 4.9, 5.9 }; /*初始化源数组*/
cout << "Size of first: " << int(first.size()) << '\n';
for (int i = 0; i < first.size(); i++)
cout << first[i] << " ";
cout << endl;
first.assign(10, 5);
cout << "Size of first: " << int(first.size()) << '\n';
for (int i = 0; i < first.size(); i++)
cout << first[i] << " ";
cout << endl;
return 0;
}
输出:
Size of second: 5
1.9 2.9 3.9 4.9 5.9
Size of second: 10
5 5 5 5 5 5 5 5 5 5
void assign( std::initializer_list<T> ilist );//(C++11 起) (C++20 前)
constexpr void assign( std::initializer_list<T> ilist );//(C++20 起)
功能:将当前vector内容替换成ilist中内容
// vector assign
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<double> first{ 1.9, 2.9, 3.9, 4.9, 5.9 }; /*初始化源数组*/
vector<double> second{ 1, 2, 3, 4, 5 }; /*初始化源数组*/
cout << "Size of first: " << int(first.size()) << '\n';
for (int i = 0; i < first.size(); i++)
cout << first[i] << " ";
cout << endl;
second.assign({1, 2, 3});
cout << "Size of second: " << int(second.size()) << '\n';
for (int i = 0; i < second.size(); i++)
cout << second[i] << " ";
cout << endl;
return 0;
}
输出:
Size of first: 5
1.9 2.9 3.9 4.9 5.9
Size of second: 3
1 2 3
2.reserve和resize
vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector的capacity同时也增加了它的size!
原因如下:
reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素。加入新的元素时,要调用push_back()/insert()函数。
resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了,因此当加入新的元素时,用operator[]操作符,或者用迭代器来引用元素对象。此时再调用push_back()函数,是加在这个新的空间后面的。
两个函数的参数形式也有区别的,reserve函数之后一个参数,即需要预留的容器的空间;resize函数可以有两个参数,第一个参数是容器新的大小, 第二个参数是要加入容器中的新元素,如果这个参数被省略,那么就调用元素对象的默认构造函数。
(1)resize用法:
void resize(size_type new_size);
void resize(size_type new_size, const value_type& value);
void resize(size_type new_size);
①new_size>size()时,如果new_size>capacity(),那么size()和capacity()大小同时变为new_size,重新分配出来的空间执行默认初始化。如果size()<new_size<capacity(),size()大小变为new_size、capacity()不变,size()和new_size之间的数据执行默认初始化,new_size和capacity()之间的数据无法访问。
②new_size<size()时,size()大小变为new_size,capacity()不变,new_size和capacity()之间数据无法访问
例1:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
std::vector<int> vec(5); // 创建一个大小为5的向量
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
for (int i = 0; i < vec.size(); i++)
{
vec[i] = 2*i;
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
vec.resize(7); // 改变向量的大小为7
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
vec.resize(5); // 改变向量的大小为5
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
return 0;
}
输出:
0 0 0 0 0
0 2 4 6 8
vec.size():5, vec.capacity():5
===========================================================
0 2 4 6 8 0 0
vec.size():7, vec.capacity():7
===========================================================
0 2 4 6 8
vec.size():5, vec.capacity():7
===========================================================
void resize(size_type new_size, const value_type& value);
①new_size<size()时,结果是容器的size()减小到new_size,删除n之后的元素,capacity()不变
②size()<new_size<capacity()时,结果是容器的size()增加到new_size,增加的元素值初始化为value,capacity()不变
③new_size>capacity()时,结果是容器的size()和capacity()增加到new_size,增加的元素值初始化为value
例2:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
std::vector<int> vec(5); // 创建一个大小为5的向量
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
for (int i = 0; i < vec.size(); i++)
{
vec[i] = 2*i;
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
vec.resize(3,6); // 改变向量的大小为3
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
vec.resize(4, 6); // 改变向量的大小为3
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
vec.resize(7, 6); // 改变向量的大小为3
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
return 0;
}
输出:
0 0 0 0 0
0 2 4 6 8
vec.size():5, vec.capacity():5
===========================================================
0 2 4
vec.size():3, vec.capacity():5
===========================================================
0 2 4 6
vec.size():4, vec.capacity():5
===========================================================
0 2 4 6 6 6 6
vec.size():7, vec.capacity():7
===========================================================
(2)reserve用法:
void reserve( size_type new_cap );//(C++20 前)
constexpr void reserve( size_type new_cap );//(C++20 起)
①new_cap < capacity()时,size与capacity均不发生改变;
②new_cap > capacity()时,此时会重新分配一块空间,使得capacity扩容至n,size不发生改变。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
std::vector<int> vec(5); // 创建一个大小为5的向量
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
for (int i = 0; i < vec.size(); i++)
{
vec[i] = 2*i;
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
vec.reserve(3); // 改变向量的大小为3
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
vec.reserve(9);
for (int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}
cout << endl;
cout << "vec.size():" << vec.size() << ", vec.capacity():" << vec.capacity() << endl;
cout << "===========================================================" << endl;
return 0;
}
输出:
0 0 0 0 0
0 2 4 6 8
vec.size():5, vec.capacity():5
===========================================================
0 2 4 6 8
vec.size():5, vec.capacity():5
===========================================================
0 2 4 6 8
vec.size():5, vec.capacity():9
===========================================================
例1:
#include<iostream>
#include<vector>
using namespace std;
void printVector(vector<int>& v)
{ //利用迭代器打印 v
int i = 0;
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
{
cout << *it << " ";
i++;
if(i%10 == 0)
cout << endl;
}
cout << endl;
}
int main(int argc, char* argv[])
{
vector<int> myVec;
myVec.reserve(100); // 新元素还没有构造,
// 此时不能用[]访问元素
for (int i = 0; i < 100; i++)
{
myVec.push_back(i); //新元素这时才构造
}
myVec.resize(102); // 用元素的默认构造函数构造了两个新的元素
myVec[100] = 1; //直接操作新元素
myVec[101] = 2;
printVector(myVec);
return 0;
}
5.8综合示例
// vectorsample.cpp : 定义控制台应用程序的入口点。
//
//#include "stdafx.h"
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Student
{
public:
string m_strNO;
string m_strName;
string m_strSex;
string m_strDate;
public:
Student(string strNO, string strName, string strSex, string strDate)
{
m_strNO = strNO;
m_strName = strName;
m_strSex = strSex;
m_strDate = strDate;
}
void Display()
{
cout << m_strNO << "\t";
cout << m_strName << "\t";
cout << m_strSex << "\t";
cout << m_strDate << "\t";
}
};
class StudCollect
{
vector<Student> m_vStud;
public:
void Add(Student& s)
{
m_vStud.push_back(s);
}
Student* Find(string strNO)
{
bool bFind = false;
int i;
for (i = 0; i < m_vStud.size(); i++)
{
Student& s = m_vStud.at(i);
if (s.m_strNO == strNO)
{
bFind = true;
break;
}
}
Student* s = NULL;
if (bFind)
s = &m_vStud.at(i);
return s;
}
};
int main(int argc, char* argv[])
{
Student s1("1001", "zhangsan", "boy", "1988-10-10");
Student s2("1002", "lisi", "boy", "1988-8-25");
Student s3("1003", "wangwu", "boy", "1989-2-14");
StudCollect s;
s.Add(s1);
s.Add(s2);
s.Add(s3);
Student* ps = s.Find("1002");
if (ps)
ps->Display();
return 0;
}
代码运行实例如下:
1002 lisi boy 1988-8-25
参考:vector容器用法详解 - 蒲公英110 - 博客园 (cnblogs.com)
6.vector容器的底层实现机制
作者建议还是稍微看一下底层机制,vector主要有三个指针(迭代器) 来表示的:
●_Myfirst 和 _Mylast 可以用来表示 vector 容器中目前已被使用的内存空间;
●_Mylast 和 _Myend 可以用来表示 vector 容器目前空闲的内存空间;
●_Myfirst 和 _Myend 可以用表示 vector 容器的容量。
对于空的 vector 容器,由于没有任何元素的空间分配,因此 _Myfirst、_Mylast 和 _Myend 均为 null。
vector扩容本质
另外需要指明的是,当 vector 的大小和容量相等(size==capacity)也就是满载时,如果再向其添加元素,那么 vector 就需要扩容。vector 容器扩容的过程需要经历以下 3 步:
1.完全弃用现有的内存空间,重新申请更大的内存空间;
2.将旧内存空间中的数据,按原有顺序移动到新的内存空间中;
3.最后将旧的内存空间释放。
这也就解释了,为什么 vector 容器在进行扩容后,与其相关的指针、引用以及迭代器可能会失效的原因。
由此可见,vector 扩容是非常耗时的。为了降低再次分配内存空间时的成本,每次扩容时 vector 都会申请比用户需求量更多的内存空间(这也就是 vector 容量的由来,即 capacity>=size),以便后期使用。
vector 容器扩容时,不同的编译器申请更多内存空间的量是不同的。以 VS 为例,它会扩容现有容器容量的 50%。