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

参考资料:

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

3.3 标准库类型vector(P86)

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

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

vector<int> ivec;

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

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

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

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

image-20230913140124556

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

列表初始化vector对象

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

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

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

创建指定数量的元素

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

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

值初始化

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

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

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

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

某些情况下,初始化的真实含义依赖于传递初始值时使用的时花括号还是圆括号。如果使用圆括号,可以理解为提供的值是用来构造 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"

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    // 错误

计算vector内对象的索引

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

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

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

3.4 迭代器介绍(P95)

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

3.4.1 使用迭代器(P95)

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

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

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<int>::iterator
auto it2 = cv.begin();    // it2的类型为vector<int>::const_iterator

为了得到 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 可行吗?不可行,因为迭代器的加法不存在。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值