您的位置:首页 > 编程语言 > C语言/C++

《C++ Primer》读书笔记第九章-1- 顺序容器概述 And 容器库概览

2017-09-18 23:59 477 查看
笔记会持续更新,有错误的地方欢迎指正,谢谢!

顺序容器概述

前言:这部分的内容在写程序的时候肯定是处处都能用到的,而且会让你的程序很简洁。本章是第三章的拓展,详细地介绍了标准库顺序容器的知识。一个容器就是一些特定类型对象的集合。

不同容器在两个方面的性能不同:

向容器中添加或删除元素;

访问容器中的元素。

详情如下:

容器特征
vector可变大小数组,支持快速随机访问,在尾部之外的位置插入或删除元素较慢。(因为在内存中元素是连续存储的,所以快速随机访问(想访问谁就访问谁)很容易,头的位置再加上你要访问的顺序就好,添加删除元素就需要移动它之后的所有元素,所以慢)
list双向链表,只支持双向顺序访问,不支持随机访问,在任意位置插入删除都快,内存中不连续,通过指针指向前后元素
deque双端队列。支持快速随机访问,在头尾位置插入或删除元素很快。
forward_list单向链表,只支持单向顺序访问,在链表任意位置插入删除都快。也就是:不支持反向容器迭代器,且其迭代器不支持递减运算。
array固定大小数组,支持快速随机访问,不能添加或删除
string与vector类似,专门用于保存字符,随机访问快,在尾部插入删除快
标准库容器性能通常优于同类数据结构。

可以看到每个容器都对性能有所侧重,都有不同的灵活度。

那我们在选择容器的时候应该怎么选呢?

除非你有很好的理由选择其他容器,否则一律用vector;

程序要求随机访问,就用vector或deque;

程序有很多小的元素,需要很大的额外开销,不要用list或forward_list,因为它们再去连其他内存开销更大;

如果程序要在容器中间插入或删除元素,应使用list或forward_list;

程序只在头尾插入删除元素,用deque。

总结:在实际应用的时候,你一定要自己衡量好性能,只要你掌握了表格中各个容器的特点,相信你能做出最合适的选择!

容器库概览

这部分讲述所有容器的共性,每个容器都定义在一个头文件中,文件名和类型名相同,例如,deque定义在头文件deque中。容器均定义为模板类,我们必须提供额外信息来生成特定的容器类型。

对容器可以保存的元素类型的限制:

顺序容器几乎可以保存任意类型的元素,包括它自己。也有一些例外,比如类类型。

例子:我要保存一个类类型的对象,而这个类没有默认构造函数,于是,我得自己给它提供一个元素初始化器:

//假定a是一个没有默认构造函数的类型
vector<a> v1(10, init); //正确:10个a类型的元素init(也就是10个a的对象)
vector<a> v2(10); //错误:没有默认构造函数,无法初始化。


迭代器

之前介绍过,这里就不介绍了。

迭代器可加可减,但forward_list不支持减,理由不用解释。

反向迭代器

大多数容器还提供反向迭代器,反向迭代器++就是上一个元素。

reverse_iterator按逆序寻址的迭代器,rbegin()是尾迭代器,crend()表示前迭代器。

要遍历的话,就从crend()递减到rbegin()。

begin和end成员

list<string> a = {"1", "2", "3"};
auto it1 = a.begin(); //此auto为list<string>::iterator
auto it2 = a.rbegin(); //list<string>::reverse_iterator
auto it3 = a.cbegin(); //list<string>::const_iterator
auto it4 = a.crbegin(); //list<string>::const_reverse_iterator


当不需要修改时,最好用cbegin。

容器定义和初始化

每个容器类型都定义了一个默认构造函数。除了array之外,其他容器的默认构造函数都会创建一个指定类型的空容器。下图已经说得很清楚了:



补充:

explicit构造函数是用来防止隐式转换的。请看下面的代码:

class Test1
{
public:
Test1(int n)//普通构造函数
{
num=n;
}
private:
int num;
};
class Test2
{
public:
explicit Test2(int n)//explicit(显式)构造函数
{
num=n;
}
private:
int num;
};
int main()
{
Test1 t1=12;//隐式调用其构造函数,成功。
Test2 t2=12;//不能隐式调用其构造函数,编译错误。
Test2 t2(12);//显式调用其构造函数,成功。这种就叫显示构造函数!!!
return 0;
}


与顺序容器大小相关的构造函数:

vector<int> a1(10, -1);//10个-1
vector<int> a2(10);//10个0
vector<string> a3(10);//10个空string


总结:如果元素类型是内置类型或有默认构造函数的类类型,可以在初始化的时候只为构造函数提供一个容器大小参数;如果没有默认构造函数,必须要显式地提供初值(具体例子可见本文“容器库概览”)。

标准库array具有固定大小:

列表初始化的数目必须等于或小于array的大小;花括号列表只能初始化不能赋值。

array的大小也是类型的一部分:

array<int, 3>; //类型是3个int的数组
array<int, 10> a1; //10个0
array<int, 3> a2 = {1, 2, 3}; //列表初始化
array<int, 3> a3 = {5}; //正确的。a3为5, 0, 0


我们不能对内置数组进行拷贝,但array可以(可能这也是为什么搞出array的原因):

array<int, 3> a = {1, 2, 3};//列表初始化
array<int, 3> copy = a;//拷贝


赋值和swap

vector<int> a1 = {1, 2, 3, 4, 5};
a1 = {1, 2}; //这样正确,a1变为1,2。

array<int, 5> b1 = {1, 2, 3, 4, 5};
b1 = {1}; //这样错误。只有array,花括号列表只能初始化不能赋值。


使用swap:

vector<string> s1(10);
vector<string> s2(20);
swap(s1, s2); //交换后,s1有20个元素,s2有10个元素。


容器大小操作

名称含义
size返回容器中元素个数
empty只在size为0时返回true
max_size返回一个大于或等于该容器所能容纳的最大元素个数

关系运算符

容器装的元素类型支持关系运算,我们才能来用它。比如:内置类型

vector<int> v1 = {1, 3, 5, 7, 9, 12};
vector<int> v2 = {1, 3, 9};
vector<int> v3 = {1, 3, 5, 7};
v1 < v2;
v1 > v3;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ c++primer 读书笔记