前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++の容器vector

C++の容器vector

作者头像
leoay
发布2019-12-27 11:17:18
6770
发布2019-12-27 11:17:18
举报
文章被收录于专栏:leoayleoay

题图:NoCopy

字数:2492 | 3分钟读完我3小时的思考

C++のvector类

上一篇文章C++のstring类,我们讲了一下C++中的string类,简单梳理了string对象的创建方式,数据操作等,今天我们继续介绍C++中另外一个概念vector。

那么,什么是vector呢?简单来讲,它就是一个可以用来装东西的容器。我们首先来看一下怎么用vector这个类吧。

如果想要使用vector,我们需要包含以下头文件

#include<vector>

由于vector属于std命名空间范围的类,因此还需要指定命名空间,如下:

using std::vector;

或者在需要的地方直接在vector前指定命名空间:

std::vector...

vector为什么是容器呢?因为它能装其他的对象,这有点像数组,但是远比数组强大。vector基本可以装所有类型的对象,而数组大多数情况下只能存数字或字符。

关于容器,我们后面详细介绍,以后我们还会学习很多种类型的容器,大体上分为两类,序列型容器和关联型容器。而我们今天所说的vector即是序列型容器。所谓序列型就是指vector这个东西存数据的时候按照先后顺序一个一个存,可以把它想象成数据结构中的栈这个概念。

除此之外,vector还是一个模板类,模板这个概念是泛型编程里面的概念,这我们后面学习泛型编程的时候再详细说(暂时不了解模板,丝毫不影响我们学习vector的使用)。现在我们可以把它理解为用vector创建对象时,是有一个模板的,可以根据传入参数的数据类型创建对象,这也是vector能够存储基本所有内置类型对象和类类型对象的原因。

但是有一点,vector并不能存引用型变量,引用仅仅是一个变量的别名,而不带表实体。

我们先来看一下怎么用vector创建对象吧。

代码如下所示:

...
class A
...

vector<int> vec_int; // 创建整型的vector
vector<A> a_vec; // 创建一个用于存A的对象的vector
vector<vector<string>> file; //创建一个用于存储vector的vector,前者中又存着string对象

我们需要注意的是,vector并不是像int, string等具体的类型,vector并不是类型,所以不能直接使用:

vector vec;

这种方式创建对象,而应该指明vector中所存元素的类型。

下面我们来看一下怎么样初始化一个vector吧。由于C++中vector属于类模板,因此其初始化也需要用类模板的形式。如下:

vector<T> v1             初始化一个空的vector v1,其中的元素类型为T
vector<T> v2(v1)         使用v1中的所有元素初始化一个vector v2
vector<T> v3 = v1        使用v1中的所有元素初始化一个vector v3
vector<T> v4(n)          定义vector v4,并用n个T类型的元素初始化
vector<T> v5{a, b, c}    使用{a, b, c}三个元素初始化v5
vector<T> v6={a, b, c}   使用{a, b, c}三个元素初始化v6

下面我们简单用几行示例代码看一下怎么样定义和初始化vector对象吧:

//Cpp_Vector参考代码

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

int main() {
  vector<int> v1;   //创建一个int型vector v1

  vector<int> v2(10);  //v2中存有10个0
  vector<int> v3(10, 1);  //v2中存有10个1

  vector <std::string > v4(10);  //v3中存有10个""
  vector <std::string > v5(10, "Hello");  //v3中存有10个"Hello"

  vector <std::string> v6{ "Hello", "World", "!" }; //v6中存有"Hello"、"World"、"!"
  return 0;
}

既然,vector是用来存对象的,那么我们怎么来访问它里面存储的对象呢?C++11中提供了5种方式,分别是:

at              访问vector中特定的元素
operator[]      通过索引访问指定的元素
front           访问vector中第一个元素
back            访问vector中最后一个元素
data            获取vector首地址

我们看一下代码:

std::cout << v6.at(0) << std::endl;
std::cout << v6.front() << std::endl;
std::cout << v6.back() << std::endl;
std::cout << v6[0] << std::endl;
std::cout << v6.data() << std::endl;
std::cout << *v6.data() << std::endl;
std::cout << v6.data()[1] << std::endl;

v6上面初始化时,值为{ "Hello", "World", "!" },执行上面的代码后输出

Hello
Hello
!
Hello
000002789C905620
Hello
World

注意上面的data()方法是在C++11标准才出现的,C++98标准是没有的。

对于一个vector对象,既然它是容器,便有一些跟容量有关的操作,C++11标准中提供了7个操作:

empty     判断vector对象是否为空
size      获取vector对象的大小
max_size  vector可以存的最大元素个数,与内存大小有关
reserve   改变capacity的大小
resize    改变size的大小
capacity  当前vector的容量
shrink_to_fit 释放vector中没有使用的内存

我们看一下示例代码:

std::cout << v6.empty() << std::endl;
std::cout << v6.size() << std::endl;
std::cout << v6.max_size() << std::endl;
std::cout << v6.capacity() << std::endl;
    
v6.reserve(100);
std::cout << v6.capacity() << std::endl;
std::cout << v6.size() << std::endl;

v6.resize(200);
std::cout << v6.size() << std::endl;

v6.shrink_to_fit();
std::cout << v6.capacity() << std::endl;

运行结果如下:

0
3
461168601842738790
3
100
3
200
200

可以看出,vector的size与capacity并不是一个概念。size指当前vector中所有元素实际占用的空间大小,而capacity指当前vector的容量,可能vector中根本没有存这么多的元素。只是代表代码申请了多少内存。所以,才会有以上的输出结果。

下面,我们说一下有关vector对象的修改相关方法。C++11中有以下几种:

clear      清空vector中的内容
insert     在某个位置插入元素
emplace    在某个位置插入元素
erase      擦除元素
push_back  向vector中存入元素,size增加
emplace_back 在vector最后一个元素后面插入一个新元素
pop_back   从vector推出一个元素,size减小
resize     改变size的大小
swap       交换两个vector的内容

我们写一段代码演示一下:

vector <int> v_int1{ 1, 2, 3 };
vector <int> v_int2{ 4, 5, 6 };
v_int1.insert(v_int1.begin(), 8);
v_int1.emplace(v_int1.begin(), 9);
v_int1.push_back(2);
v_int1.emplace_back(12);
v_int1.pop_back();
v_int1.swap(v_int2);
v_int1.erase(v_int1.begin());

上面的代码中我们看见几处v_int1.begin(), 这里其含义就是获取v_int1的起始迭代器。关于什么是迭代器,我们下面来说。

vector中有迭代器的概念,基本所有集合类都有。那么迭代器是什么呢?它给我们提供了访问vector元素的方法,类似于指针,可以通过迭代器访问和修改vector中的元素。那么,怎么样使用迭代器呢?

首先,我们需要获取vector中的迭代器,如下:

v_int1.begin()   获取v_int1中的起始迭代器
v_int1.end()     获取v_int1中的结尾迭代器

可以通过上面的代码获取vector中的起始和结尾迭代器。

那么怎么样通过迭代器更改vector中的元素呢?如下:

*(v_int1.begin() + 1) = 9;

说到这里,可能有的朋友就要担心了,这样直接获取到一个可以随便修改vector的迭代器会不会不安全呢?万一代码中我们错误地修改了迭代器索引到的值,而实际上并不是我们想要的怎么办呢?

是的,这样的确不安全,所以vector也提供了只读迭代器的获取方式cbegin()和cend(), c的含义就是const。

所以,下面的代码,编译器是会报错的:

*(v_int1.cbegin() + 2) = 8;

一般来说,我们在代码中我们不会直接指定迭代器的类型,根据第一篇的C++自动类型推导及其他,我们一般使用auto获取迭代器的类型,示例代码如下:

for (auto it = v_int1.cbegin();it != v_int1.cend() && !it->empty(); ++it)
cout << *it << endl;

这里,我们先列一下有关迭代器的运算吧(iter为迭代器,上面的it):

*iter  对元素解引用,得到元素的值,此处为第一个元素的值
++iter  迭代器自增,等价与itr+=1
--iter  迭代器自减,等价于iter-=1
iter1 == iter2  判断两个迭代器是否相等
iter1 != iter2  判断两个迭代器是否不等
iter1 > iter2   判断iter1是否大于iter2,此外还有<,<=,>=

今天的vector我们就介绍到这里,仅仅是容器的一个小开始,后面的序列化容器还有deque、list、array、string等,你没看错,string也是序列化容器。以后讲到容器的时候详细做个对比。如果你对这篇文章有什么疑问,欢迎评论区留言,我会一一解答,如果有什么错误之处,还请指正。下篇文章,我们简单说一下array。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 leoay 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档