array
1.at()
与[]
at()函数在访问指定元素前会进行越界检查,超出界限会抛出异常
[]不会越界检查,越界后会返回一个随机值
可以通过两者修改元素的值
2.size()
=max_size
3.array.fill(const T& value)
,将 array 中所有元素的值修改为 value
vector
theSize,theCapacity,max_size
theSize:已申请的连续空间中已经使用的大小
theCapacity:已申请的连续空间中可以使用的大小
max_size:整个内存空间中可以使用的大小
max_size>>theCapacity>=theSize
按需两倍扩容
vector 有三个迭代器 start、finish 和 end_of_storage 分别指向连续空间的头部、被使用部分的尾部和整块连续空间的尾部
在使用过程中,有备用空间(finish 之后,end_of_storage 之前)用来存储插入到尾部的元素。当备用空间不足时,vector 按照当前总容量的两倍进行扩容
扩容的实际步骤是:
- 重新配置两倍容量的空间
- 移动原内容到新空间,在原内容后构造新内容元素
- 释放原空间
1 | if (theSize == theCapacity) |
1 | std::vector<int> vector = {0, 1, 2}; // 实际空间是3 |
根据初始化 vector 时,申请了三个大小的空间;当插入第四个元素时,扩容至 3 的两倍
3. 手动扩容与压缩
reserve(size_type new_cap)
:手动扩展空间至 new_cap 大小
shrink_to_fit()
:释放未使用的空间
1 | vector.reserve(10); |
4.pop_bash()
:移除末尾元素,不改变空间大小
1 | void pop_back(){ |
假设 size=4,capacity=8,移除后 size=3,capacity=8
5.clear()
:清除空间元素,同上,不改变空间大小
6.push_back()
与emplace_back()
的区别
push_back()
:先构建一个临时对象,然后调用拷贝构造函数(c++11 后调用转移构造函数)放入备用空间,实现插入,最后再释放临时变量
emplace_back()
:在备用空间原地构造,不会触发拷贝构造(转移构造),节省资源
deque
- 逻辑上来看,是一整段连续空间,实际上动态链接多段的定量连续空间(称作缓冲区),在缓冲区中存储数据,空间不足时分配新的空间并链接在头部或尾部
- deque 的主控是一个连续空间,所谓的 map(与容器中的 map 不同)。通过这个 map 存储指向缓冲区的指针。使用 deque 时可以指定缓冲区大小,默认大小是 512 字节
- deque 通过迭代器重载 ++ 和- -,来实现『整体连续』的假象
map
:deque 的本体,存储每个 buffer 的地址start
:指向第一个 buffer 的 iteratorfinish
:指向最后一个 buffer 的 iteratoriterator
:
node
:指向本 buffer 在 map 中的位置,方便在本 buffer 遍历结束后找到下一个 buffer 的 位置
first
:指向本 buffer 的空间中的开头地址
finish
:指向本 buffer 的空间中的结尾地址
cur
:指向 buffer 中某个具体的元素重点:finish 的 cur 指向最后一个元素的下一个位置
- 尽可能使用 vector,需要对 deque 进行排序时,先复制进 vector,调用 sort,再复制回 deque
- 适配器
stack
,queue
通过复合的方式,默认以deque
作为基础容器(也可以选择list
),调用 deque 的成员函数,完成自己的功能stack
:先进后出的栈,也可以使用vector
作为基础容器queue
:先进先出的队列两者不允许遍历,说明无法在中间插入元素
List
- 结点类有两个指针,
next
和prev
,分别指向下一个结点和上一个结点
1 | template<typename T> |
- list 实际上是一个环状双向链表,巧妙的利用一个空结点记录 list 的头部和尾部
1 | template<class Object> |
list 中的node指针
指向一个空置 data 的 node,这个 node 利用 next 和 prev 记录 list 的头部和尾部
我在自己设计双向链表的时候,想到的是直接用两个指针来分别指向头部和尾部,现在学到了利用环状链表来解决问题
在利用继承的情况下,这个 data 甚至不会空置
1 | template<class Object> |
这样,只需要在list
类中声明一个list_node_base<Object>
类型的成员变量,就可以用来指向表头和表尾
3. list 利用 default constructor 构建一个空 list
1 | public: |
4.iterator
通过继承const_iterator
,来实现通过迭代器修改data
5. 迭代器中需要有一个const list<Object>
类型的指针,用来保存 list 的位置,以便需要时可以找到 list
6. list 的迭代器通过操作符重载,完成前移、后移和取值等操作
7.erase(iterator itr)
修改被删除结点的前驱和后继结点的指针,使之相互链接。delete
被删除指针并自减theSize
,返回了被删除结点后面的结点
通过调用erase()
方法来实现其他方法
1 | void pop_front(){ |
- list 的析构函数,通过
clear()
删除所有结点后,再delete
指向链表的 node 成员变量,完成析构
1 | ~list(){ |