什么?还不懂 c++vector 的用法,你凭什么勇气来的!
作者:良知犹存
转载授权以及围观:欢迎添加微信公众号:羽林君
前言
今天继续肝 C++,一入 C++深似海。越学越有意思。今天给大家带来一篇 c++vector 的介绍,难以置信这篇文章写了我三天,不过总算整理完毕,现在分享给大家。
模板类 vector 和 array 是数组的替代品。模板类 vector 类似于 string 类,也是一种动态数组。 在 c++ 中,vector 是一个十分有用的容器。它能够像容器一样存放各种类型的对象,简单地说,vector 是一个能够存放任意类型的动态数组,能够增加和压缩数据。
在 C++ primer plus 这本书中关于 vector 不是进行一次性介绍的,而是分别在不同板块使用 vctor 而去介绍的,今天我就融合起来介绍一些 vector 的使用。
vector 做一个模板类
C++语言既有类模板,也有函数模板,其中 vector 是一个类模板。只有对 C++有一定深入的理解才能写出模板。
模板本身不是类或是函数,相反可以将模板看作编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程叫做实例化,当使用模板时,需要指出编译器应把类或者函数实例化成何种类型。
对于类模板来说,我们通过提供一些额外的信息,来指定模板到底实例化成什么样的类,需要提供哪些信息由模板决定。而提供信息的方式如下所示,即在模板名字后面跟一对尖括号,在括号上面放上信息。
以 vector 为例子:
上面的例子中,编译器根据模板 vector 生成了三种不同的类型:vector<int>,vector<Sales_item>和 vector<vector<string>>。
vector 是模板而非类型,由 vector 生成的类型必须包含 vector 中的元素类型,例如 vector<int>,int 就是 vector 元素的类型。
vector 能容纳大部分类型的对象作为参数,但是因为引用不是对象,所以不存在包含引用的 vector。
vector<int &> int; //是错误的
定义和初始化 vector 类型
看到第一个初始化例子,初始化了一个空 vector,看上去空 vector 好像没什么用处。但是别忘了,vector 是一个数组,在程序运行中,我们是可以很高效的往 vector 对象中添加元素。事实上,vector 最常用方式就是先定义一个空 vector,然后当运行时获取到元素,再逐一添加。
当然也可以在定义 vector 对象时指定元素的初始值。例如,允许一个 vector 对象的元素拷贝给另一个 vector 对象。此时,新 vector 对象的元素就是原 vector 对象对应的副本。注意两个 vector 对象的类型必须相同。
列表初始化 vector 对象
列表初始化即使用花括号括起来的 0 个或多个初始元素值被赋给 vector 对象:
创建指定数量的元素:
还可以用 vector 对象容纳的元素数量和所有元素的统一初始值来初始化 vector 对象:
vector 迭代器功能
要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。
不过和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时返回迭代器的成员。比如,这些类型都拥有名为 begin 和 end 的成员,其中 begin 负责返回指向第一个元素的迭代器,
end 成员则负责返回指向容器"尾元素的下一个位置"的迭代器。这样的迭代器
指示的是容器的一个不存在的"尾后"元素。
举例子:依次输出 text 的每一行直至遇到第一个空白行为止
注: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)",例如:
在这上面有进一步优化的空间就是使用 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 使用,上面例子就可以这么表示:
其他的 vector 操作
不能用下标形式添加元素
此外还有好多 vector 属于容器的操作,大家可以参考容器使用的函数,都是一样的:具体使用另一位朋友写的很详细,我就不多做赘述了,大家可以去看看《vector容器!》
vector 当作参数
在 C++里很多时候我们会遇到函数想返回两个以上结果的情况,这时候可以用数组(vector)、类来作为容器返回,也可以声明一个全局变量的数组,将数值存放在数组里解决。
使用引用来解决,将 vector 的引用在函数间传递
这是一个例子,假设我要传入一个数,我的函数的功能是返回这个数后面十个数的序列。
使用 vector 注意事项:
1、如果你要表示的向量长度较长(需要为向量内部保存很多数),容易导致内存泄漏,而且效率会很低;
2、Vector 作为函数的参数或者返回值时,需要注意它的写法:
其中的“&”绝对不能少!!
文件处理和 vector 应用
主要是尝试在文件中记录和读取信息,中间用到了 vector,C++导出 excel 表格的过程太过繁琐,所以这里直接用很简单的方法导出一个.csv 的文本文件,该文件也可用 excel 打开。
outFile.open("data.csv", ios::out);前面的双引号内容为 csv 文件路径,若没有输入文件路径,则在编译器默认路径下生成一个 csv 文件。‘
这就是我分享的 vector 的一些介绍和使用,如果大家有什么更好的思路,也欢迎分享交流哈。
*—**END*—
推荐阅读
【2】嵌入式底层开发的软件框架简述 【3】CPU中的程序是怎么运行起来的 必读【4】C++的匿名函数(lambda表达式)【5】阶段性文章总结分析
本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。
更多分享,扫码关注我
版权声明: 本文为 InfoQ 作者【良知犹存】的原创文章。
原文链接:【http://xie.infoq.cn/article/27142073d3086e72f6f8b7a14】。文章转载请联系作者。
评论