110.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对象中添加元素
(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语句中,预存了尾部位置相关的标记位置的值,增减元素会改变其值。
4.vector类常用的函数
4.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
4.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
4.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
4.4遍历函数
reference at(int pos) :返回pos位置元素的引用
reference front() : 返回首元素的引用
reference back() : 返回尾元素的引用
iterator begin() : 返回向量头指针,指向第一个元素
iterator end() : 返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin() : 反向迭代器,指向最后一个元素
reverse_iterator rend() : 反向迭代器,指向第一个元素之前的位置
4.5判空函数
bool empty() const:判断向量是否为空,若为空,则向量中无元素
4.6大小函数
int size() const:返回向量中元素的个数
int capacity() const:返回当前向量所能容纳的最大元素值
int max_size() const:返回最大可允许的vector元素数量值
4.7其他函数
void swap(vector&):交换两个同类型向量的数据
void assign(int n,const T& x):设置向量中第n个元素的值为x
void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素
4.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)
标签:begin,iterator,int,元素,vector,110,向量 From: https://www.cnblogs.com/codemagiciant/p/17567357.html