点击并拖拽以移动

作者:良知犹存

转载授权以及围观:欢迎添加微信公众号:羽林君

点击并拖拽以移动

前言

今天继续肝C++,一入C++深似海。越学越有意思。今天给大家带来一篇c++vector的介绍,难以置信这篇文章写了我三天,不过总算整理完毕,现在分享给大家。

模板类vector 和 array是数组的替代品。模板类vector 类似于string类,也是一种动态数组。 在 c++ 中,vector 是一个十分有用的容器。它能够像容器一样存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。

在C++ primer plus 这本书中关于vectir不是进行一次性介绍的,而是分别在不同板块使用vctor而去介绍的,今天我就融合起来介绍一些vector的使用。

点击并拖拽以移动

vector做一个模板类

C++语言既有类模板,也有函数模板,其中vector是一个类模板。只有对C++有一定深入的理解才能写出模板。

模板本身不是类或是函数,相反可以将模板看作编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程叫做实例化,当使用模板时,需要指出编译器应把类或者函数实例化成何种类型。

对于类模板来说,我们通过提供一些额外的信息,来指定模板到底实例化成什么样的类,需要提供哪些信息由模板决定。而提供信息的方式如下所示,即在模板名字后面跟一对尖括号,在括号上面放上信息。

以vector为例子:

vector<int> ivec; //ivec保存int类型的对象
vector<Sales_item> Sales_vec;//保存Sales_item类型的对象
vector<vector<string>> file; //一个二维数组,该元素的vector对象

点击并拖拽以移动

上面的例子中,编译器根据模板vector生成了三种不同的类型:vector<int>,vector<Sales_item>和vector<vector<string>>。

vector是模板而非类型,由vector生成的类型必须包含vector中的元素类型,例如vector<int>,int就是vector元素的类型。

vector能容纳大部分类型的对象作为参数,但是因为引用不是对象,所以不存在包含引用的vector。

vector<int &> int; //是错误的

定义和初始化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,看上去空vector好像没什么用处。但是别忘了,vector是一个数组,在程序运行中,我们是可以很高效的往vector对象中添加元素。事实上,vector最常用方式就是先定义一个空vector,然后当运行时获取到元素,再逐一添加。

当然也可以在定义vector对象时指定元素的初始值。例如,允许一个vector对象的元素拷贝给另一个vector对象。此时,新vector对象的元素就是原vector对象对应的副本。注意两个vector对象的类型必须相同。

vector<int> ivec; //初始状态为空
vector<int> ivec2(ivec); //把ivec的值拷贝给ivec2
vector<int> ivec3 = ivec; //把ivec的元素拷贝给ivec3
vector<string> svec(ivec2); //错误:svec的元素时string对象,不是int

点击并拖拽以移动

列表初始化vector对象

列表初始化即使用花括号括起来的0个或多个初始元素值被赋给vector对象:

vector<string> article   = {"a","an","the"};
vector<string> article1 = ("a","an","the");//错误 不能放置于圆括号内

点击并拖拽以移动

创建指定数量的元素:

还可以用vector对象容纳的元素数量和所有元素的统一初始值来初始化vector对象:

vector<int> ivec(10,-1); //10个int类型的元素,每个都被初始化为-1
vector<string> svec(10,"hi!");//10个sting类型的元素,每个都被初始化为"hi!"

点击并拖拽以移动

点击并拖拽以移动

vector迭代器功能

要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。

不过和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时返回迭代器的成员。比如,这些类型都拥有名为begin和end的成员,其中begin负责返回指向第一个元素的迭代器,

auto b = v.begin(), c = v.end(); //b表示v的第一个元素 c表示v尾元素的下一个位置

点击并拖拽以移动

end成员则负责返回指向容器"尾元素的下一个位置"的迭代器。这样的迭代器

指示的是容器的一个不存在的"尾后"元素。

*iter //返回迭代器iter所指元素的应用


iter->mem  //解应用iter并获取该元素名为mem的成员,等价于(*iter).mem


++iter    //令iter指向容器中的下一个元素
--iter    //令iter指向容器的上一个元素

点击并拖拽以移动

举例子:依次输出text的每一行直至遇到第一个空白行为止

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

点击并拖拽以移动

:cbegin()和cend()是C++11新增的,它们返回一个const的迭代器,不能用于修改元素。

点击并拖拽以移动

vector当作容器

一个容器就是一些特定类型对象的集合。顺序容器类型有vector(可变大小数组,支持快速随机访问,在尾部之外的位置插入或删除元素可能很慢)、deque(双端队列,支持快速随机访问,在头尾插入/删除元素很快)、list(双向列表,只支持双向顺序访问,在list中任何位置进行插入/删除操作速度都很快)、forward_list(单向列表,只支持单向顺序访问,在列表中任何位置进行插入/删除操作速度都很快)、array(固定大小数组,支持快速随机访问,不能添加或删除元素)、string(与vector类似的容器,但专门用于保存字符,随机访问快,在尾部插入/删除速度快)。

向vector对象中添加元素

对vector对象来说,直接初始化的方式适用于三种情况:初始值已知且数量较少、初始值是另一个vector对象的副本、所有元素的初始值都一样。然后更常见的情况是:创建一个vector对象时并不清楚实际所许需要的元素个数,元素的值也无法确定。还有些时候即使元素的初值已知,但如果这些值的总量较大且各不相同,那么在创建vector对象的时候执行初始化操作也会显得过于繁琐。

举个例子:如果想创建一个vector对象令其包含从0到9共10个元素,使用列表初始化的方法很容易做到这一点;但如果vector对象所包含的元素是从0到99或者0到999呢?这时候通过列表初始化把所有元素都一一罗列出来就不太合适了。对于此例来说,更好的处理方法是先创建一个空的vector,然后在运行时再利用vector的成员函数push_back向其中添加元素。push_back负责把一个值当成vector对象的尾元素"压到(push)"vector对象的"尾端(back)",例如:

string word;
vector<string> text;//空vector对象
while(cin>>word)//cin>>word 是对word 进行赋值
{
text.push_back(word);//把word添加到text后面
}

点击并拖拽以移动

在这上面有进一步优化的空间就是使用emplace_back(顺序容器(如vector、deque、list)新标准引入了三个新成员:emplace_front、emplace和emplace_back,这些操作构造而不是拷贝元素。这些操作分别对应push_front、insert和push_back,允许我们将元素放置在容器头部、一个指定位置之前或容器尾部。)

在容器尾部添加一个元素,这个元素原地构造,不需要触发拷贝构造和转移构造。而且调用形式更加简洁,直接根据参数初始化临时对象的成员。

当调用push或insert成员函数时,我们将元素类型的对象传递给它们,这些对象被拷贝到容器中。而当我们调用一个emplace成员函数时,则是将参数传递给元素类型的构造函数。emplace成员使用这些参数在容器管理的内存空间中直接构造元素。

emplace_back在引入右值引用,转移构造函数,转移复制运算符之前,通常使用push_back()向容器中加入一个右值元素(临时对象)的时候,首先会调用构造函数构造这个临时对象,然后需要调用拷贝构造函数将这个临时对象放入容器中。原来的临时变量释放。这样造成的问题是临时变量申请的资源就浪费。

所以现在我们可以用emplace_back替换push_back使用,上面例子就可以这么表示:

text.emplace_back(word);//把word添加到text后面

点击并拖拽以移动

其他的vector操作

v.empty()   //
v.size()
v[n] //返回v中第n个位置上的元素引用

点击并拖拽以移动

不能用下标形式添加元素

v[idex] = a; //错误

点击并拖拽以移动

此外还有好多vector属于容器的操作,大家可以参考容器使用的函数,都是一样的:具体使用另一位朋友写的很详细,我就不多做赘述了,大家可以去看看《vector容器!

v.capacity();//容器的容量
v.size();//返回容器中的元素个数
v.resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置;如果容器变短,则末尾超出容器长度的元素被删除
v.resize(int num, eles);
v.insert(const_iterator pos,ele);//迭代器指向位置pos插入元素els
v.erase(const_iterator pos);//删除迭代器指向的元素
v.erase(const_iterator start,const_iteartor end);//删除迭代器从start到end之间的元素
v.clear();//删除容器中所有元素

点击并拖拽以移动

点击并拖拽以移动

vector当作参数

在C++里很多时候我们会遇到函数想返回两个以上结果的情况,这时候可以用数组(vector)、类来作为容器返回,也可以声明一个全局变量的数组,将数值存放在数组里解决。

使用引用来解决,将vector的引用在函数间传递

这是一个例子,假设我要传入一个数,我的函数的功能是返回这个数后面十个数的序列。

#include<iostream>
#include<vector>
using namespace std;
/*
输入一个数,返回这个数后面的十个数字序列
注意参数的这个 & 符号不能省略
*/
void getSequence(int num,vector<int>& sequence){
for(int i=0;i<10;i++){
sequence.push_back(i+num);
}
}
int main(){
int num=9;
vector<int> sequence;
//在主调函数这边,直接传入该vector变量
getSequence(num,sequence);
//访问该vector的值的时候,也是直接访问即可
for(vector<int>::iterator it=sequence.begin();it!=sequence.end();it++){
cout<<*it<<endl;
}
}

点击并拖拽以移动

使用vector注意事项:

1、如果你要表示的向量长度较长(需要为向量内部保存很多数),容易导致内存泄漏,而且效率会很低;

2、Vector 作为函数的参数或者返回值时,需要注意它的写法:

double Distance(vector<int>&a,vector<int>&b)

点击并拖拽以移动

其中的“&”绝对不能少!!

点击并拖拽以移动

文件处理和vector应用

主要是尝试在文件中记录和读取信息,中间用到了vector,C++导出excel表格的过程太过繁琐,所以这里直接用很简单的方法导出一个.csv的文本文件,该文件也可用excel打开。

#include<iostream>
#include<string>
#include<fstream> // 文件流
#include<sstream>
#include<vector> using namespace std; int main()
{
//写文件
ofstream outFile;
outFile.open("data.csv", ios::out);
outFile << "name" << ',' << "age"<< ',' << "hobby" << endl;
outFile << "Mike" << ',' << 18 << ','<< "paiting" << endl;
outFile << "Tom" << ',' << 25 << ','<< "football" << endl;
outFile.close();
//写文件
ifstream inFile("data.csv", ios::in);
string lineStr;
vector< vector<string> > strArray; //vector 类型文string
while(getline(inFile, lineStr)) // 从 inFile 中读取一行,放到 lineStr 中
{
cout<< lineStr<<endl; stringstream ss(lineStr); //读取内容放置在 ss流 中, 括号相当于初始化
string str;
vector<string> lineArray;
// 按照逗号分隔
while(getline(ss, str, ',')) // ss 中, 按照 “,” 逗号分割将ss 分割成一个个str
{
lineArray.push_back(str); // 将字符串放置到 line Array
cout<< str<<endl;
}
strArray.push_back(lineArray);
}
getchar();
return 0;
}

点击并拖拽以移动

outFile.open("data.csv", ios::out);前面的双引号内容为csv文件路径,若没有输入文件路径,则在编译器默认路径下生成一个csv文件。‘

这就是我分享的vector的一些介绍和使用,如果大家有什么更好的思路,也欢迎分享交流哈。

*—**END*—

推荐阅读

【1】嵌入式的我们为什么要学ROS

【2】嵌入式底层开发的软件框架简述 【3】CPU中的程序是怎么运行起来的 必读 【4】C++的匿名函数(lambda表达式) 【5】阶段性文章总结分析

本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得

点击并拖拽以移动

更多分享,扫码关注我

什么?还不懂c++vector的用法,你凭什么勇气来的!的更多相关文章

  1. c++中vector的用法详解

    c++中vector的用法详解 vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间 ...

  2. 看完此文还不懂NB-IoT,你就过来掐死我吧...【转】

    转自:https://www.cnblogs.com/pangguoming/p/9755916.html 看完此文还不懂NB-IoT,你就过来掐死我吧....... 1 1G-2G-3G-4G-5G ...

  3. 看完此文还不懂NB-IoT,你就过来掐死我吧...

    看完此文还不懂NB-IoT,你就过来掐死我吧....... 1 1G-2G-3G-4G-5G 不解释,看图,看看NB-IoT在哪里? 2 NB-IoT标准化历程 3GPP NB-IoT的标准化始于20 ...

  4. STL中的Vector相关用法

    STL中的Vector相关用法 标准库vector类型使用需要的头文件:#include <vector>. vector 是一个类模板,不是一种数据类型,vector<int> ...

  5. C++学习二 vector的用法(使用sort对于vector排序)

    一.vector的介绍 vector是C++里面的一个容器,也是我们数学上面理解的向量,有一些比较常见的操作. 二.vector的定义 #include<vector> using nam ...

  6. STL vector常见用法详解

    <算法笔记>中摘取 vector常见用法详解 1. vector的定义 vector<typename> name; //typename可以是任何基本类型,例如int, do ...

  7. C++:vector的用法详解

    原文地址:http://blog.csdn.net/hancunai0017/article/details/7032383 vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于 ...

  8. 一篇文章看懂JS闭包,都要2020年了,你怎么能还不懂闭包?

     壹 ❀ 引 我觉得每一位JavaScript工作者都无法避免与闭包打交道,就算在实际开发中不使用但面试中被问及也是常态了.就我而言对于闭包的理解仅止步于一些概念,看到相关代码我知道这是个闭包,但闭包 ...

  9. 还不懂mysql的undo log和mvcc?算我输!

    最近一直没啥时间写点东西,坚持分享真的好难,也不知道该分享点啥,正好有人要问我这些东西,所以腾出点时间,写一下这个主题.同样本篇可以给读者承诺,听不懂或者没收获算我输,哈哈! 众所周知,mysql中读 ...

随机推荐

  1. Windows下使用Graalvm将Javafx应用编译成exe

    1 背景 Graalvm是Oracle推出的一款新型虚拟机,其中一个吸引人的功能是:它可以将Java代码编译成各个平台的本地代码,这些平台包括:linux.macOS.windows.iOS.andr ...

  2. 肌肤管家SkinRun V3S智能皮肤检测仪,用AI探索肌肤问题

    继肌肤管家SkinRun V3皮肤检测仪之后,肌肤管家SkinRun近期又一重磅推出的肌肤管家SkinRun V3S 智能肌肤测试仪引起了美业人的广泛关注.据了解它汇集百万皮肤数据,利用五光谱原理和人 ...

  3. Java向指定Excel写入读取数据

    今天在开发中遇到用户列表导入导出的功能实现,这里了解到使用POI函数库可以完成此任务!特此记录一下 POI Apache POI是Apache软件基金会开放的源码函数库,POI提供API给Java程序 ...

  4. 天梯赛练习 L3-016 二叉搜索树的结构 (30分)

    题目分析: 用数型结构先建树,一边输入一边建立,根节点的下标为1,所以左孩子为root*2,右孩子为root*2+1,输入的时候可用cin输入字符串也可用scanf不会超时,判断是否在同一层可以判断两 ...

  5. NodeJS之npm、cnpm、npx、yarn

    一.npm 1,概念 npm 是 Node.js 官方提供的包管理工具,他已经成了 Node.js 包的标准发布平台,用于 Node.js 包的发布.传播.依赖控制.npm 提供了命令行工具,使你可以 ...

  6. 记一次使用logmnr查找操作人流程

    经常遇到开发的需求,帮我查一下是谁修改了表里面的记录,是谁对表进行了DDL操作,此类问题可以使用logmnr解决 1.根据操作时间定位归档日志 SELECT name FROM V$ARCHIVED_ ...

  7. ASP.NET Core错误处理中间件[3]: 异常处理器

    DeveloperExceptionPageMiddleware中间件错误页面可以呈现抛出的异常和当前请求上下文的详细信息,以辅助开发人员更好地进行纠错诊断工作.ExceptionHandlerMid ...

  8. HATEOAS的简单认识

    HATEOAS: 超媒体作为应用程序状态引擎(HATEOAS)是REST应用程序体系结构的一个组件,它将其与其他网络应用程序体系结构区分开来. 使用HATEOAS,客户端与网络应用程序交互,其应用程序 ...

  9. JavaScript中的构造函数和原型!

    JavaScript中的原型! 原型的内容是涉及到JavaScript中的构造函数的 每一个构造函数都有一个原型对象!prototype 他的作用是 共享方法!还可以扩展内置对象[对原来的内置对象进行 ...

  10. CentOS 7.2系统安装步骤

    CentOS 7.2系统安装步骤 1.把系统U盘插到服务器上,然后启动服务器进入BIOS界面选择U盘启动. 根据服务器的不同,进入BIOS界面的按钮也不一样,主流的有F10.F11.F12.F2.ES ...