• 《C++ Primer》第3章 字符串、向量和数组(二)


    参考资料

    • 《C++ Primer》第5版
    • 《C++ Primer 习题集》第5版

    3.3 标准库类型vector(P86)

    vector 表示对象的序列,其中所有对象的类型相同,每个对象都有一个与之对应的索引。vector 容纳着其他对象,所以常被称作“容器”。

    vector 是一个类模板,使用模板时,我们需要提供一些信息来指定模板需要实例化成什么类,提供信息的方式为:在模板名字后跟一对尖括号,在括号内放入信息:

    vector<int> ivec;
    
    • 1

    由于引用不是对象,所以不存在包含引用的 vector

    vector 中的元素似乎不能具有 cv 属性,c++ - Does C++11 allow vector? - Stack Overflow

    在早期的 C++ 版本中,嵌套 vector 需要写成 vector > ,而非 vector>

    3.3.1 定义和初始化vector对象(P87)

    image-20230913140124556

    最常见的初始化方法就是定义一个空 vector 。使用拷贝初始化时,必须保证两个 vector 对象的类型相同。

    列表初始化vector对象

    C++11 为了 vector 提供了列表初始化方法:

    vector<string> articles{"a", "an", "the"};
    
    • 1

    列表初始化只能使用花括号

    创建指定数量的元素

    可以提供 vector 对象容纳的元素数量统一初始值来初始化 vector 对象:

    vector<int> ivec(10, -1);
    
    • 1

    值初始化

    如果我们只提供元素数量而不提供元素初始值,库会创建一个值初始化的元素初值,并把它赋给容器中的所有元素。如果 vector 对象的元素是内置类型,则元素初始值为 0 ,如果元素是某种类类型,则元素由类默认初始化

    这种初始化方法有两个限制:

    • 如果有些类要求提供初始值,只提供元素数量就无法完成初始化操作。
    • 只能使用直接初始化。
    vector<int> ivec1(10);    // 正确
    vector<int> ivec2 = 10;    // 错误
    
    • 1
    • 2

    列表初始值还是元素数量?

    某些情况下,初始化的真实含义依赖于传递初始值时使用的时花括号还是圆括号。如果使用圆括号,可以理解为提供的值是用来构造 vector 对象的;如果使用花括号,可以理解为我们列表初始化 vector 对象,所以初始化过程会尽可能把花括号内的值当作元素初始值的列表,在无法执行列表初始化时才会考虑其他方式。

    vector<int> v1;    // 空
    vector<int> v2(10);    // 10个值为0的元素
    vector<int> v3(10, 42);    // 10个值为42的元素
    vector<int> v4{10};    // 1个值为10的元素
    vector<int> v5{10, 42};    // 两个元素,值分别为10和42
    vector<string> v6{10};    // 10个空字符串
    vector<string> v7{"a", "an"};    // 两个元素,值分别为"a"和"an"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.3.2 向vector对象中添加元素(P90)

    push_back 可以把一个值当成 vector 对象的为元素压到 vector 对象的尾端。

    C++ 要求 vector 能在运行时高效添加元素,所以在创建 vector 时指定其大小是没有必要的,且往往会导致性能下降。例外情况是所有元素的值都一样。

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

    范围 for 循环不应改变其遍历序列的大小。

    3.3.3 其他vector操作(P91)

    image-20230913143535067

    size 的返回类型是由 vector 定义的 size_type 类型:

    vector<int>::size_type    // 正确
    vector::size_type    // 错误
    
    • 1
    • 2

    计算vector内对象的索引

    不能使用下标形式添加元素

    vector<int> ivec(10, 0);
    ivec[10] = 1;    // 错误
    
    • 1
    • 2

    下标运算符用于访问已经存在的元素。使用下标访问不存在的元素不会被编译器发现,而是在运行时产生一个不可预知的值,并常常导致缓冲区溢出

    3.4 迭代器介绍(P95)

    所有标准库容器都可以使用迭代器(iterator)访问其元素。迭代器类似于指针类型,提了对象的间接访问,迭代器可以访问某个元素,也能从一元素移动到另一个元素。迭代器有无效有效之分,有效的迭代器指向某个元素,或者指向尾元素的下一个位置

    3.4.1 使用迭代器(P95)

    有迭代器的类型同时拥有返回迭代器类型的成员

    vector<int> ivec(10, 0);
    auto b = ivec.begin(), e = ivec.end();
    
    • 1
    • 2

    begin 用于返回指向第一个元素的迭代器,end 用于返回指向尾后元素的迭代器。特别地,如果 vector 为空,则 beginend 返回的是同一个迭代器。

    迭代器运算符

    image-20230913150510928

    试图解引用尾后迭代器和无效迭代器是未定义行为。

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

    所有标准库容器的迭代器都定义了 ==!= ,但只有少数定义了 < 运算符,所以我们常常使用 it != v.end() 作为循环条件。

    迭代器类型

    拥有迭代器的标准库类型使用 iteratorconst_iterator 来表示迭代器。const_iterator 和常量指针类似。

    beginend运算符

    beginend 的返回类型有对象是否是常量决定:

    vector<int> v;
    const vector<int> cv;
    
    auto it1 = v.begin();    // it1的类型为vector::iterator
    auto it2 = cv.begin();    // it2的类型为vector::const_iterator
    
    • 1
    • 2
    • 3
    • 4
    • 5

    为了得到 const_iterator 类型,C++11 引入了 cbegincend

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

    it->mem 等价于 (*it).mem

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

    任何可能改变 vector 对象容量(如 push_back )的操作都会导致该对象的迭代器失效。

    使用了迭代器的循环体,不要向迭代器所属的容器添加元素。

    3.4.2 迭代器运算(P99)

    所有标准库容器的迭代器都支持 ++==!=stringvector 的迭代器还提供了更多额外的迭代器运算

    image-20230913154717627

    迭代器的算术运算

    两个迭代器相减得到的是类型为 difference_type带符号整数

    使用迭代器运算

    image-20230913155009097

    mid = (beg+end) /2 代替 mid = beg + (end-beg)/2 可行吗?不可行,因为迭代器的加法不存在。

  • 相关阅读:
    神经网络与强化学习:揭示AI的超能力
    参加微软学生开发者峰会,了解Azure和GitHub……
    详解 UDP 协议
    Leetcoder Day29| 贪心算法part03
    TCP和UDP C#代码实战
    Java面试题总结(三)
    基于LEX的词法分析实验
    SVG鼠标漫游
    基于JSP的网络教学平台的设计与实现
    java数据结构与算法刷题-----LeetCode58:最后一个单词的长度
  • 原文地址:https://blog.csdn.net/MaTF_/article/details/132856246