【C++】vector的使用

11 篇文章 1 订阅

在这里插入图片描述

🔥个人主页: Forcible Bug Maker
🔥专栏: STL || C++

前言

本篇博客主要内容:STL库中vector的介绍以及vector用法的讲解

在string部分,我们讲了很大篇幅的内容,一部分原因是因为初次接触STL,当你理解一个时,就会发现其他的内容都是相通的;另一原因就是,string所提供的接口确实太多。
本篇将要介绍和讲解的vector,算是string学习的奖励内容,它的本质是我们所实现的动态顺序表。动态顺序表的内容可以参考我之前写的初阶数据结构:初阶数据结构-顺序表和链表(C语言)

在C++中,一般不用数组,而使用vector。

🌈关于vector

在这里插入图片描述
vector是表示可变大小数组的序列容器。

就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。

本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。

vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。

因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。

与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。

🔥默认成员函数在这里插入图片描述

学vector,首先需要知道它都有什么构造,以及有什么样的赋值运算符重载。

构造函数(constructor)

在这里插入图片描述

构造一个vector对象,可以使用以下四种方式对构造的对象初始化。

default (1)

explicit vector (const allocator_type& alloc = allocator_type());

这是std::vector的默认构造函数。其创建了一个不含任何元素的空vector对象。可选参数alloc是一个分配器对象,用于指定内存分配模型(往后会展开,这里不用过于考究,大多数时候使用默认值即可)。
其中explicit关键字阻止了隐式类型转换。
fill (2)

explicit vector (size_type n, const value_type& val = value_type(),
                 const allocator_type& alloc = allocator_type());

构造一个含有n个val值的vector容器
alloc一般使用缺省值。
explicit防止参数进行隐式类型转换。
range (3)

template <class InputIterator>
         vector (InputIterator first, InputIterator last,
                 const allocator_type& alloc = allocator_type());

按迭代器区间[first,last)内容顺序构造vector对象
alloc参数一般使用缺省值。
copy (4)

vector (const vector& x);

构造一个和x对象的拷贝
允许隐式类型转换。

代码案例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	// 默认构造
	vector<int> v1;

	// 填充构造
	vector<int> v2(10, 4);
	for (int i = 0; i < v2.size(); i++) {
		cout << v2[i] << " ";
		v2[i] = i;
	}
	cout << endl;

	// 迭代器区间构造
	vector<int> v3(v2.begin(), v2.begin() + 5);
	for (int i = 0; i < v3.size(); i++)cout << v3[i] << " ";
	cout << endl;

	// 拷贝构造
	vector<int> v4(v3);
	for (int i = 0; i < v4.size(); i++)cout << v4[i] << " ";
	cout << endl;

	// 隐式类型转换
	vector<double> v5({ 1.2,1.4,1.5,5.5 });
	for (int i = 0; i < v5.size(); i++)cout << v5[i] << " ";
	cout << endl;

	return 0;
}

在这里插入图片描述

析构函数(destructor)

在这里插入图片描述
在vector对象声明周期结束时自动调用,释放内存空间。

赋值运算符重载(operator=)

在这里插入图片描述

vector& operator= (const vector& x);

这里的operator=赋值重载的和string类似,是一个深拷贝。
同样支持隐式类型转换。

🔥迭代器接口

在这里插入图片描述

和string迭代器的使用方式类似。可以通过迭代器接口获取vector对象的迭代器。

可以将迭代器想象成一个指针,指向容器中的元素。

在这里插入图片描述
上图中,vec是一个vector对象。

begin和end

在这里插入图片描述
在这里插入图片描述

iterator begin();
const_iterator begin() const;

iterator end();
const_iterator end() const;

两个接口函数用于获取正向迭代器。
begin用于获取指向首元素正向迭代器,end用于获取指向尾元素下一位正向迭代器

代码案例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> vec({ 1,2,3,4,5,6,7 });
	vector<int>::iterator it = vec.begin();
	while (it != vec.end()) {
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

rbegin和rend

reverse_iterator rbegin();
const_reverse_iterator rbegin() const;

reverse_iterator rend();
const_reverse_iterator rend() const;

两个接口函数用于获取反向迭代器。
begin用于获取指向尾元素反向迭代器,end用于获取指向首元素上一位反向迭代器

代码案例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> vec({ 1,2,3,4,5,6,7 });
	vector<int>::reverse_iterator rit = vec.rbegin();
	while (rit != vec.rend()) {
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

cbegin,cend,crbegin和crend

这四个是C++11的新增语法,为了支持const类型的vector对象而重载进来的。和非const类型的迭代器接口的本质区别是,通过它们获取的迭代器只能读取元素内容,不能改动
string中也提到过,这里不多赘述。

🔥元素获取

在这里插入图片描述
vector对象的元素同时支持以下几种元素获取方式

operator[ ]重载,at访问,front获取首元素,back获取尾元素

operator[]at获取元素的区别:当下标越界时,使用operator[]获取报错at获取抛异常

代码案例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> vec({ 1,2,3,4 });
	for (int i = 0; i < vec.size(); i++) {
		cout << vec[i] << " ";
		cout << vec.at(i) << " ";
	}
	cout << endl;

	cout << vec.front() << " ";
	cout << vec.back() << endl;
	return 0;
}

在这里插入图片描述

🔥容量接口

在这里插入图片描述

通过这些接口可以获取当前对象容量信息。

size

在这里插入图片描述

size_type size() const;

返回vector中的元素个数。
这个vector对象返回的元素个数size并不一定等于capacity。

代码案例:

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> myints;
	cout << "0. size: " << myints.size() << '\n';

	for (int i = 0; i < 10; i++) myints.push_back(i);
	cout << "1. size: " << myints.size() << '\n';

	myints.insert(myints.end(), 10, 100);
	cout << "2. size: " << myints.size() << '\n';

	myints.pop_back();
	cout << "3. size: " << myints.size() << '\n';

	return 0;
}

在这里插入图片描述

capacity

在这里插入图片描述

size_type capacity() const;

获取vector对象的容量大小。当你对vector对象执行元素插入,空间不够时,会自动改变capacity并扩容。

代码案例:

// 比较size和capacity
#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> myvector;

	// set some content in the vector:
	for (int i = 0; i < 100; i++) myvector.push_back(i);

	cout << "size: " << (int)myvector.size() << '\n';
	cout << "capacity: " << (int)myvector.capacity() << '\n';

	return 0;
}

在这里插入图片描述
有关size和capacity的大小问题,不同编译器的实现不同,运行上面代码过程中产生的结果也可能不同。
任何情况下,size都是小于等于capacity的

reserve

在这里插入图片描述

void reserve (size_type n);

reserve可以用来改变vector对象的容量capacity
但不会影响size以及当前对象已有元素。

代码案例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> v({ 1,2,3,4 });
	cout << v.size() << endl;
	cout << v.capacity() << endl;
	cout << endl;

	v.reserve(30);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

	return 0;
}

在这里插入图片描述

resize

在这里插入图片描述

void resize (size_type n, value_type val = value_type());

resize可以用来改变vector的size

当n小于size时,将内容减少到其前n个元素,删除超出它们的元素。

当n大于size时,对于vector对象中已经存在的元素不做改动,对于尚未定义的元素空间用val填充。

当n大于capacity时,则会重新分配内存空间(扩容)。

代码案例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> v({ 1,2,3,4 });
	cout << v.size() << endl;
	cout << v.capacity() << endl;
	cout << endl;

	v.resize(15, -1);
	cout << v.size() << endl;
	cout << v.capacity() << endl;

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

	return 0;
}

在这里插入图片描述

reserve负责开辟空间,当知道需要多少空间时,可以帮助缓解vector增容的代价。
resize在开辟空间时会初始化,同时影响size。

reserve和resize都不会进行缩容,如果有缩容需要,可以使用shrink_to_fit();

empty

在这里插入图片描述
判断vector对象是否为空,为空返回1,非空返回0。简单说:判断size是否为0

🔥vector对象增删查改

在这里插入图片描述

push_back

在这里插入图片描述

尾插一个元素,capacity不够时自动扩容

pop_back

在这里插入图片描述
尾删一个元素

代码案例:

#include<vector>
using namespace std;
int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (int i = 0; i < v.size(); i++) {
		cout << v[i] << " ";
	}
	cout << endl;

	cout << v[v.size() - 1] << " ";
	v.pop_back();
	cout << v[v.size() - 1] << " ";
	v.pop_back();
	cout << v[v.size() - 1] << " ";
	
	cout << endl;
	cout << v.size() << endl;
	
	return 0;
}

在这里插入图片描述

insert

在这里插入图片描述

insert用于在指定位置插入元素。

此方法有三个重载形式:

single element (1)

iterator insert (iterator position, const value_type& val);

将元素val插入到迭代器position指向的元素之前
如果position指向end();迭代器,则将元素添尾插
同时,此函数存在一个返回值,指向新插入的元素。

fill (2)

void insert (iterator position, size_type n, const value_type& val);

此方法在position指向元素之前插入n个val元素
无返回值

range (3)

template <class InputIterator>
    void insert (iterator position, InputIterator first, InputIterator last);

此方法将位于[first,last)区间的元素按顺序插入到position指向的元素之前
无返回值。

代码案例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> v({ 1,2,3,4 });

	vector<int>::iterator it = v.begin() + 1;

	// (1)
	it = v.insert(it, 100);
	for (auto e : v) cout << e << " ";
	cout << endl;

	// (2)
	v.insert(it, 3, 0);
	for (auto e : v) cout << e << " ";
	cout << endl;

	// (3)
	v.insert(v.end(), v.begin() + 1, v.begin() + 4);
	for (auto e : v) cout << e << " ";
	cout << endl;

	return 0;
}

在这里插入图片描述

erase

在这里插入图片描述
可以删除一个迭代器指向的元素,也可以删除一个迭代器区间内的元素

iterator erase (iterator position);

iterator erase (iterator first, iterator last);

这两个函数都存在一个返回值,返回被删除部分的下一个元素(如果没有下一个元素,则为end())的迭代器。

代码案例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> v({ 1,2,3,4 });

	vector<int>::iterator it = v.begin() + 1;
	cout << *it << " ";

	it = v.erase(it);
	cout << *it << endl;

	it = v.erase(it, v.end());
	if (it == v.end())
		cout << "it == v.end()" << endl;

	return 0;
}

在这里插入图片描述

clear

在这里插入图片描述
删除vector对象中的所有元素,同时将size置零。

代码案例:

#include<iostream>
#include<vector>
using namespace std;
int main()
{
	vector<int> v({ 1,2,3,4 });
	cout << v.size() << endl;
	for (auto e : v)cout << e << " ";
	cout << endl;

	v.clear();
	cout << v.size() << endl;
	for (auto e : v)cout << e << " ";
	cout << endl;
	
	return 0;
}

在这里插入图片描述

结语

vector相比string来说接口少了很多,也有很多逻辑相同之处,所以有些内容就没有细讲。使用oj刷题时,常常会用到vector,所以熟练掌握vector接口是非常必要。更详细的内容大家也可以参考STL文档,后期还有一些比较重要的非成员函数重载,都会纳入我们的讲解范围。
本篇博客的内容到这里就结束了,博主后续还会继续分享STL相关的内容,感谢大家的支持!♥

  • 130
    点赞
  • 127
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 182
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Forcible Bug Maker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值