点击并拖拽以移动

作者:良知犹存

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

点击并拖拽以移动

前言

今天继续肝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. CQRS与Event Sourcing之浅见

    引言 DDD是近年软件设计的热门.CQRS与Event Sourcing作为实施DDD的一种选择,也逐步进入人们的视野.围绕这两个主题,软件开发的大咖[Martin Fowler].[Greg You ...

  2. python 2.7.11 环境安装

    0  安装依赖: yum install zlib zlib-devel readline-devel sqlite-devel bzip2-devel openssl-devel gdbm-deve ...

  3. Pandas数据分析练手题(十题)

    数据集下载地址:https://github.com/Rango-2017/Pandas_exercises --------------------------------------------- ...

  4. nginx日志按天切割

    要求:以天为单位进行日志文件的切割,如host.access_20150915.log, 日志保留最近10天的, 超过10天的日志文件则进行删除. nginxcutlogs.sh脚本内容: #!/bi ...

  5. 使用K8s的一些经验和体会

    坑 Java应用程序的奇怪案例 ​ 在微服务和容器化方面,工程师倾向于避免使用 Java,这主要是由于 Java 臭名昭著的内存管理.但是,现在情况发生了改变,过去几年来 Java 的容器兼容性得到了 ...

  6. leetcode 1593. 拆分字符串使唯一子字符串的数目最大(DFS,剪枝)

    题目链接 leetcode 1593. 拆分字符串使唯一子字符串的数目最大 题意: 给你一个字符串 s ,请你拆分该字符串,并返回拆分后唯一子字符串的最大数目. 字符串 s 拆分后可以得到若干 非空子 ...

  7. oracle创建恢复编录(recovery catalog)

    1.在要作为恢复编录的数据库创建用户 create user rman identified by oracle default tablespace system temporary TABLESP ...

  8. 使用Intelij 运行Android 程序导致的无法安装

    前几天的时候更换了开发工具开发Android ,终于不用忍受Android studio 的各种卡顿了.我决定使用一段时间Intelij 开发Android. 之前的程序代码在运行的时候也出现了异常, ...

  9. 关于安装版JDK1.8 1.7更改多个JDK环境变量 不生效

    配置maven(apache-maven-3.3.9)时提示异常:'mvn' 不是内部或外部命令,上网查找得知 它不支持jdk1.8,所以重新安装jdk1.7,后来就出现下面的问题 现象: 当使用安装 ...

  10. Zju1100 Mondriaan

    题目描述 有一个m行n列的矩阵,用1*2的骨牌(可横放或竖放)完全覆盖,骨牌不能重叠,有多少种不同的覆盖的方法? 你只需要求出覆盖方法总数mod p的值即可. 输入格式 三个整数数n,m,p,m< ...