C++ Primer读书笔记——第3章-字符串、向量和数组(中)

来源:互联网 发布:c语言编写俄罗斯方块 编辑:程序博客网 时间:2024/05/16 01:37

3.3 标准库类型vector

vector是模板而非类型,由vector生成的类型必须包含vector中元素的类型,例如:
vector<int> ivec;               // ivec保存int类型的对象vector<Sales_item> Sales_vec;   // 保存Sales_item类型的对象vector<vector<string>> file;    // 该向量的元素是vector对象
    大多数(非引用)内置类型和类类型都可以构成vector对象,甚至组成vector的元素也可以是vector。

3.3.1 定义和初始化vector对象

                 初始化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个重复的元素,每个元素的值都是valvector<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<string> svec;    // 默认初始化,svec不含任何元素...

列表初始化vector对象

    C++11提供了另外一种为vector对象的元素赋初值的方法,即列表初始化。
vector<string> v1{"a", "an", "the"};    // 列表初始化vector<string> v1("a", "an", "the");    // 错误

创建指定数量的元素

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

值初始化

    通常情况下,可以只提供vector对象容纳的元素数量,此事库会创建一个值初始化的(value-initialized)    元素初值,并赋给容器的所有元素。例:
vector<int> ivec(10);           // 10个元素,每个都初始化为0vector<string> svec(10);        // 10个元素,每个都是空string对象
    对这种初始化有两个特殊限制:    1:有些类要求必须明确地提供初始值;    2:如果只提供了元素的数量而没有设定初始值,只能使用直接初始化。

列表初始值还是元素数量

    通常使用花括号或圆括号可以区分上述这些含义:
vector<int> v1(10);         // v1有10个元素,每个的值都是0vector<int> v2{10};         // v2有1个元素,该元素的值是10vector<int> v3(10, 1);      // v3有10个元素,每个的值都是1vector<int> v4{10, 1};      // v4有2个元素,值分别是10和1
    如果用的是圆括号,可以说提供的值是用来构造(construct)vector对象的。    如果用的是花括号,可以表述成我们想列表初始化(list initialize)该vector对象。    另一方面,如果初始化时使用了花括号的形式但是提供的值又不能用来列表初始化,    就要考虑用这样的值来构造vector对象了:
vector<string> v5{"hi"};            // 列表初始化:v5有一个元素,值为"hi"vector<string> v6("hi");            // 错误:不能使用字符串字面值构建vector对象vector<string> v7{10};              // v7有10个默认初始化的元素vector<string> v8{10, "hi"};        // v8有10个值为"hi"的元素

3.3.2 想vector对象中添加元素

利用vector的成员函数push_back向其中添加元素。push_back负责把一个值当成vector对象的尾元素“压到(push)”vector对象的“尾端(back)”。如:
vector<int> v2;             // 空vector对象for(int i = 0; i != 100; ++i)      v2.push_back(i);      // 依次把整数值放到v2尾端                            // 循环结束后v2有100个元素,值从0到99
// 从标准输入中读取单词,将其作为vector对象的元素存储string word;vector<string> text;            // 空vector对象while(cin >> word) {    text.push_back(word);       // 把word添加到text后面}
关键概念:vector对象能高效增长        C++标准要求vector应该能在运行时高效快速地添加元素。一般来说,        除了创建的vector中所有元素的值都一样,否则更有效的方法是:        先定义一个空的vector对象,再在运行时向其中添加具体值。

向vector对象添加元素蕴含的编程假定

必须要确保所写的循环正确无误,特别是在循环有可能改变vector对象容量的时候如果循环体内部包含有向vector对象添加元素的语句,则不能使用范围for循环。

注意:范围for语句体内不应改变其所遍历序列的大小

3.3.3 其他vector操作

            vector支持的操作v.empty()               如果v不含有任何元素,返回真;否则返回假v.size()                返回v中元素的个数v.push_back(t)          向v的尾端添加一个值为t的元素v[n]                    返回v中第n个位置上元素的引用v1 = v2                 用v2中元素的拷贝替换v1的元素v1 = {a,b,c... }        用列表中元素的拷贝替换v1中的元素v1 == v2                v1和v2相等当且仅当它们的元素数量相同且对应位置的元素值都相同v1 != v2<, <=, >, >=            顾名思义,以字典顺序进行比较

Note:要使用 size_type,需首先指定它是有哪种类型定义的:

vector<int>::size_type          // 正确vector::size_type               // 错误

计算vector内对象的索引

使用下标运算符能获取到指定的元素,下标的类型是相应的size_type类型。

不能用下标形式添加元素

vector<int> ivec;   // 空vector对象for(decltype(ivec.size()) ix = 0; ix != 10; ++ix){    ivec[ix] = ix;          // 严重错误:ivec不包含任何元素,                            // 不能通过下标去访问任何元素    ivec.push_back(ix);     // 正确:添加一个新元素,该元素的值是ix}
注意:vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素提示:只能对确知已存在的元素执行下标操作!确保下标合法的一种有效手段就是尽可能使用范围for语句

3.4 迭代器介绍

类似于指针类型,迭代器也提供了对对象的间接访问。使用迭代器可以访问某个元素,也能从一个元素移动到另外一个元素。有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一个位置;其他所有情况都属于无效。

3.4.1 使用迭代器

和指针不一样,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员,如:begin():返回指向第一个元素的迭代器;  end():返回指向尾元素的下一个位置的迭代器(尾后迭代器)。

Note:如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器。

一般来说,我们不清楚迭代器的准确类型,所以通常使用auto关键字定义。

迭代器运算符

      标准容器迭代器的运算符*iter           返回迭代器iter所指元素的引用iter->mem       解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem++iter          令iter指示容器中的下一个元素--iter          令iter指示容器中的上一个元素iter1 == iter2  判断两个迭代起是否相等,如果两个迭代器指示的是同一个元素iter1 != iter2  或者它们是同一个容器的尾后迭代器,则相等;反之,不相等

将迭代器从一个元素移动到另外一个元素

迭代器使用递增(递减)运算符从一个元素移动到下一个(上一个)元素。

Note:因为end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或解引用。

迭代器类型

一般来说,我们无需知道迭代器的精确类型,那些拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器的类型:
vector<int> iterator it;    // it能读写vector<int>的元素string::iterator it2;       // it2能读写string对象中的字符vector<int>::const_iterator it3;  // it3只能读元素,不能写元素string::const_iterator it4;       // it4只能读字符,不能写字符
const_iterator和常量指针差不多,能读取但不能修改它所指的元素值。

begin和end运算符

begin和end返回的具体类型有对象是否是常量决定:  常量:const_iterator;非常量:iterator。而C++11新标准引入了两个新函数:cbegin和cend:操作类似于begin和end,不过无论对象本身是否是常量,返回值都是const_iterator。

结合解引用和成员访问操作

用迭代器访问对象成员:  (*it).empty()    // 解引用it,然后调用结果对象的empty成员  it -> empty()    // 与(*it).empty()表达意思相同

某些对vector对象的操作会使迭代器失效

一:不能在范围for循环中向vector对象添加元素;二:任何一种可能改变vector对象容量的操作,比如push_back,都会使该vector对象的迭代器失效。

注意:但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。

3.4.2 迭代器运算

      vector和string迭代器支持的运算iter + n        迭代器加上一个整数值仍得一个迭代器,迭代器指示的新位置与原                来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个                元素,或者指示容器尾元素的下一个位置iter - n        迭代器减去一个整数值仍得一个迭代器,迭代器指示的新位置与原                来相比向后移动了若干个元素。结果迭代器或者指示容器内的一个                元素,或者指示容器尾元素的下一个位置iter1 += n      迭代器加法的复合赋值语句,将iter1加n的结果赋给iter1iter1 -= n      迭代器减法的复合赋值语句,将iter1减n的结果赋给iter1iter1 - iter2   两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右                侧的迭代器向前移动差值个元素后将得到左侧的迭代器。参与运算                的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一                个位置>、 >=、 <、<=   迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代                器所指位置之前,则说前者小于后者。参与运算的两个迭代器必须                指向的是同一个容器中的元素或者尾元素的下一位置
0 0
原创粉丝点击