顺序容器--vector的详解

时间:2022-06-16 04:36:14

前言

博客编写人:Willam
博客编写时间:2017/3/11
博主邮箱:2930526477@qq.com(有志同道合之人,可以加qq交流交流编程心得)

1、vector介绍

vector是一种顺序容器,所谓的容器就是一个保存一组类型相同的数据的集合。容器有顺序容器和关联容器之分,所谓的顺序容器指的是元素排列次序与元素的值无关,而是由元素添加到容器里的次序决定。

vector有如下几个特点:


- 支持数据的随机存取
- 在集合的末尾添加元素很快,为常数时间
- 在集合中间添加元素很慢,O(n)
- 使用模板实现了vector,所以它可以存放任意类型的数据
- 有些容器提供了stable iterator保证,但是vector没有,它的某些操作会造成它的iterator失效。

2、vector的头文件

#include<vector>
using std::vector;

3、vector的构造函数

vector() //无参构造函数

vector(size_type num,const type & val) //构造一个初始放入num个值为val的元素的Vector

vector(const vector & form) //构造一个与vector from 相同的vector,其实这个是拷贝构造函数

vector(input_iterator start,input_iterator end) // 构造一个初始值为[start,end)区间元素的Vector(注:半开区间).

构造函数的使用示例:

第一种:

#include<iostream>
#include<vector>

using namespace std;

int main() {
//定义一个使用无参构造的vector
vector<int> test;

}

第二种:

#include<iostream>
#include<vector>

using namespace std;

int main() {
//建立一个含有10个1 的vector
vector<int> test(10,1);

}

第三种:

#include<iostream>
#include<vector>

using namespace std;

int main() {
//建立一个含有10个1 的vector
vector<int> test(10,1);
//使用拷贝构造函数,构建一个test的复制品
vector<int> copy(test);

}

第四种:

#include<iostream>
#include<vector>

using namespace std;

int main() {
//建立一个含有10个1 的vector
vector<int> test(10,1);
//使用两个迭代器指定的范围内的容器的值来初始化copy
//至于cbegin和cend后续会介绍
vector<int> copy(test.cbegin(),test.cend());

}

如果不知道什么迭代器,请阅读这篇博客:迭代器介绍

4、vector的运算符介绍

 ==   //比较两个容器的内容是否相等,相等则返回true
!= //比较两个容器的内容是否不相等,不相等返回true
<= //比较左边的容器的内容是否小于等于右边,如果是,则返回true
>= //比较左边的容器的内容是否大于等于右边,如果是,则返回true
< //比较左边的容器的内容是否小于右边,如果是,则返回true
> //比较左边的容器的内容是否大于右边,如果是,则返回true
[] //下标操作符,用于读取容器中特定位置的内容

vector的运算符主要就是有关两个容器内容的比较,下面我们给出容器内容比较的一个规则:

  • 如果两个容器内容长度不同,但是短的那个的内容和长的那个的前面的内容全部相同,那么就是容器长度长的那个比较大
  • 如果两个容器内容的长度相同而且内容也全部相同,则两个容器相等
  • 除了前面的两种情况,两个容器的大小由他们第一个不同的位置上的元素决定。

示例如下:

#include<iostream>
#include<vector>

using namespace std;

int main() {
//建立4个vector,使用列表初始化,相当与调用拷贝构造函数
vector<int> v1 = {1,5,6,7,8};
vector<int> v2 ={1,5};
vector<int> v3 ={1,5,2 };
vector<int> v4 ={1,5,6,7,8 };
cout << "v1==v4 " << (v1 == v4) << endl;
cout << "v1>v2 " << (v1 > v2) << endl;
cout << "v1>v3 " << (v1 > v3) << endl;
system("pause");
return 0;


}

输出:

顺序容器--vector的详解

5、vector读取元素方法

operator[]  //使用下标操作符读取对应的容器的内容

at() //使用at函数读取对应容器的内容

front() //返回容器的第一个元素的内容

back() //返回容器的最后一个元素的内容
  • operator[]使用示例:
#include<iostream>
#include<vector>

using namespace std;

int main() {
//建立4个vector,使用列表初始化,相当与调用拷贝构造函数
vector<int> v1 = {1,5,6,7,8};
//输出容器中下标为2中存放的内容,
//下标从0开始,所就是6
cout << v1[2] << endl;
//修改容器中下标为2中存放的内容因为它返回的是
//该值的引用
v1[2] = 10;
cout << v1[2] << endl;
system("pause");
return 0;
}

输出:
顺序容器--vector的详解

  • at函数使用示例
#include<iostream>
#include<vector>

using namespace std;

int main() {
//建立4个vector,使用列表初始化,相当与调用拷贝构造函数
vector<int> v1 = {1,5,6,7,8};
//输出容器中下标为2中存放的内容,
//下标从0开始,所就是6
cout << v1.at(2) << endl;
//修改下标为2中的内容,因为它返回的是
//该值的引用
v1.at(2) = 10;
cout << v1.at(2) << endl;
system("pause");
return 0;
}

输出:
顺序容器--vector的详解

  • front函数和back函数的使用示例:
#include<iostream>
#include<vector>

using namespace std;

int main() {
//建立4个vector,使用列表初始化,相当与调用拷贝构造函数
vector<int> v1 = {1,5,6,7,8};
//输出容器中下第一个元素,
cout << v1.front() << endl;
//输出容器的最后一个元素
cout << v1.back() << endl;
system("pause");
return 0;
}

输出:
顺序容器--vector的详解

使用思考:
对于运算符[]和at函数的选择,我们应该选择at函数,这是因为at函数它添加了溢出检查的代码,比[]安全,另外,再补充个小知识:at内部函数就是使用[]实现的,front和back函数分别通过(* begin())和*(end()-1)实现。

5、vector的迭代器

如果不明白迭代器,可以访问我的另外一篇博客:迭代器的介绍

begin()   //返回一个iterator类型的变量,它指向vector存放第一个元素变量的地址
end() //返回一个iterator类型的变量,它指向vector最后一个元素的下一个位置

cbegin() //返回一个const_iterator类型的变量,它指向vector存放第一个元素变量的地址
cend() //返回一个const_iterator类型的变量,它指向vector最后一个元素的下一个位置

rbegin() //返回一个reverse_iterator类型的变量,它指向vector最后一个元素
rend() //返回一个reverse_iterator类型的变量,它指向vector第一个元素的前一个位置

crbegin() //返回一个const_reverse_iterator类型的变量,它指向vector最后一个元素
crend() //返回一个const_reverse_iterator类型的变量,它指向vector第一个元素的前一个位置

代码示例:

#include<iostream>
#include<vector>

using namespace std;

int main() {
//建立4个vector,使用列表初始化,相当与调用拷贝构造函数
vector<int> v1 = {1,5,6,7,8};
cout << "begin和end的使用:" << endl;
cout << *v1.begin() << endl; //相当于front函数
//cout << *v1.end() << endl; 这个代码会出错的
//因为end函数指向一个不属于v1的空间
//下面这个才是指向v1最后一个元素
cout << *(v1.end() - 1) << endl;


cout << endl;
cout << "rbegin和rend的使用:" << endl;
cout << *v1.rbegin() << endl;
//cout << *v1.rend() << endl; 这个代码会出错的
//因为rend函数指向一个不属于v1的空间
//下面这个才是指向v1翻转后的最后一个元素
cout << *(v1.rend()-1) << endl;

system("pause");
return 0;
}

输出:
顺序容器--vector的详解

6、遍历vector容器的方法

#include<iostream>
#include<vector>
using namespace std;

int main() {
vector<int> v1 = {1,2,3,4,5};
//方法1
for (int i = 0; i < v1.size(); i++) {
cout << v1[i] << " ";
}
cout << endl;

for (std::vector<int>::iterator k = v1.begin(); k < v1.end(); ++k)
{
cout << *k << " ";
}
cout << endl;

system("pause");
return 0;
}

输出:
顺序容器--vector的详解

对应遍历的效率问题,可以参考我的另外一篇博客:访问vector元素的效率比较

7、vector新增和删除元素的方法

push_back( )  //添加元素到当前vector末尾

pop_back() //删除最最尾端的元素

emplace_back() //c++ 11标准:插入一个值到vector末尾,相当于push_back

emplace() //c++ 11 新标准:插入一个或多个值到vector内,相当于insert

insert() //插入一个或多个元素至 vector 內的任意位置。

erase() //删除一个或多个元素

clear() //清空所有元素

各个函数的具体使用示例:

  • push_back:
#include <vector>
#include <iostream>

int main()
{
/*
使用这个函数的时候,要考虑到一种情况就是:我们的容器的迭代器会出现失效的情况,这个主要的原因就是因为
我们的在添加新的成员的时候,会出现容器的最大容量小于当前的元素的个数,这个
时候我们就要重新为vector分配新的空间,从而会造成之前保存的迭代器失效。
*/

std::vector<int> numbers;

numbers.push_back(42);
numbers.push_back(314159);

for (int i : numbers) { // c++11 range-based for loop
std::cout << i << '\n';
}

return 0;
}

输出:

42
314159
  • pop_back:
#include <vector>
#include <iostream>

int main()
{
std::vector<int> numbers = {45,46};

numbers.pop_back();
for (int i : numbers) { // c++11 range-based for loop
std::cout << i << '\n';
}
system("pause");
return 0;
}

输出:

45
  • emplace_back():
#include <vector>
#include <iostream>

struct Test {
int a;
Test(int a1) {
a = a1;
}
};

int main()
{
//emplace_back是c++ 11新定义的一种用于替代push_back
//因为vector很多时候,需要我们保存一些类或者带构造函数的结构
//那么如果是使用push_back的话,我们需要新生成一个临时的对象,然后
//在拷贝到vector中,但是emplace_back不用,它可以直接调用构造函数,
//新建一个对象,保存到vector中
std::vector<Test> numbers;

numbers.emplace_back(45);
numbers.emplace_back(4);
//numbers.push_back(Test(5)); //push_back的方式
for (Test i : numbers) { // c++11 range-based for loop
std::cout << i.a << '\n';
}
system("pause");
return 0;
}

输出:

45
4
  • emplace():
#include <vector>
#include <iostream>

struct Test {
int a;
Test(int a1) {
a = a1;
}
};

int main()
{
//emplace是c++ 11新定义的一种函数,它的使用方法就好比push_back对应insert,
//emplace_back对应emplace

std::vector<Test> numbers;

numbers.emplace(numbers.cbegin(), 45);

for (Test i : numbers) { // c++11 range-based for loop
std::cout << i.a << '\n';
}
system("pause");
return 0;
}

输出:

45
  • insert():
#include <vector>
#include <iostream>

struct Test {
int a;
Test(int a1) {
a = a1;
}
};

int main()
{
/*
insert有很多版本,下面只列举其中一种
iterator insert( iterator pos, const T& value );
iterator insert( const_iterator pos, const T& value );
iterator insert( const_iterator pos, T&& value );
void insert( iterator pos, size_type count, const T& value );
iterator insert( const_iterator pos, size_type count, const T& value );
template< class InputIterator >
void insert( iterator pos, InputIterator first, InputIterator last);
template< class InputIterator >
iterator insert( const_iterator pos, InputIterator first, InputIterator last );
iterator insert( const_iterator pos, std::initializer_list<T> ilist );
*/

std::vector<Test> numbers;
numbers.insert(numbers.cbegin(), Test(4));

for (Test i : numbers) { // c++11 range-based for loop
std::cout << i.a << '\n';
}
system("pause");
return 0;
}

输出:

4
  • erase()
#include <vector>
#include <iostream>


int main( )
{
/*
iterator erase( iterator pos );
iterator erase( const_iterator pos );

iterator erase( iterator first, iterator last );
iterator erase( const_iterator first, const_iterator last );
*/

//初始化vector的另外一种方式,
std::vector<int> c{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto &i : c) {
std::cout << i << " ";
}
std::cout << '\n';

c.erase(c.begin());

for (auto &i : c) {
std::cout << i << " ";
}
std::cout << '\n';

c.erase(c.begin()+2, c.begin()+5);

for (auto &i : c) {
std::cout << i << " ";
}
std::cout << '\n';
}

输出:

0 1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 6 7 8 9

8、vector用于操作大小的函数


1. c.size() 返回当前的元素数量
2. c.empty() 判断大小是否为0
3. c.max_size() 返回可容纳的元素的最大的数量
4. c.capacity() 返回重新分配空间前,所能容呐的元素的最大量
5. c.reserve() 重新分配vector的最大容量
6. c.shrink_to_fit() (c++ 11新标准),就是把vector中没有使用的空间给释放了

函数的使用示例:

#include <iostream>
#include<vector>
using namespace std;


int main()
{
vector<int> test = { 1,2,3,4,5,6 };
cout << "size()=" << test.size() << endl;
cout << "max_size()=" << test.max_size() << endl;
cout << "capacity()=" << test.capacity() << endl;
//重新分配最大的容量
test.reserve(100);
cout << "重新分配后的";
cout << "max_size()=" << test.max_size() << endl;
cout << "重新分配后的";
cout << "capacity=" << test.capacity() << endl;

test.shrink_to_fit(); //清除无用的内存
cout << "清除无用的内存";
cout << "capacity=" << test.capacity() << endl;

system("pause");
return 0;
}

输出如下:
顺序容器--vector的详解

现在我们可以补充一个知识:

vector 存在预分配机制。可以在元素不存在的情况下预分配一段空间,为以后的存储做准备。这段空间可以用reserve()调节。capacity()返回的值就是可以存放元素的个数。capacity() - size()就是下次重新进行空间分配前的预留元素个数。至于max_size()指的是一个vector结构可供储存元素的个数的上线,通常是由于寻址空间决定的。

所以reserve是调节capacity的值,而不是max_size的值,而且reserve会造成vector指针、迭代器失效。

测试代码

#include <iostream>
#include<vector>
using namespace std;


int main()
{
vector<int> v(4);
cout << v.capacity() << endl;
cout << v.size() << endl;

std::vector<int>::iterator test = v.begin();
cout << *test << endl;
v.push_back(1);
//因为这里它进行了内存的重新分配,所以会造成迭代器失效,下面这个代码是
//有错误的,但是编译不会出错,只是到时候我们发现解引用时
//解的是一个未被分配的内存地址
cout << *test << endl;
system("pause");
return 0;
}

输出:

4
4
0
(到这里,程序会出现异常)

9、vector的赋值


1. c1=c2 这个之前已经介绍了
2. c.assign (n,elem) 复制n个elem
3. c.assign(beg,end) 将区间[beg,end]内的元素赋值给c,可以进行不同类型的容器但是容器存放的同种类型的元素的容器复制
4. c.swap(c2) 将c1和c2元素互换,
5. swap(c1,c2) 同上,但是它是全局函数,

下面是对各个函数进行实例展示:

#include <iostream>
#include<vector>
#include<string>
using namespace std;


int main()
{
vector<int> v1 = { 1,2,3,4,5 };
cout << "v1为:" << endl;

for (auto test : v1) {
cout << test << " ";
}
cout << endl;
vector<int> v2;
v2.assign(4, 1);
cout << "v2为:" << endl;
for (auto test : v2) {
cout << test << " ";
}
cout << endl;
//进行不同容器之间的元素复制
vector<char> v3;
string str = "new";
v3.assign(str.cbegin(), str.cend());
cout << "v3为:" << endl;
for (auto test : v3) {
cout << test << " ";
}
cout << endl;

cout << "交回v1和v2" << endl;
v1.swap(v2);
cout << "v1为:" << endl;

for (auto test : v1) {
cout << test << " ";
}
cout << endl;
cout << "v2为:" << endl;
for (auto test : v2) {
cout << test << " ";
}
cout << endl;
system("pause");
return 0;
}

输出:
顺序容器--vector的详解

记住两个vector交换后,他们的迭代器不会失效,只是分别属于另外一个的容器的迭代器。