[STL] Implement "vector", "deque" and "list"
vector
“可增的”数组
vector是一块连续分配的内存,从数据安排的角度来讲,和数组极其相似。
不同的地方就是:
(1) 数组是静态分配空间,一旦分配了空间的大小,就不可再改变了;
(2) vector是动态分配空间,随着元素的不断插入,它会按照自身的一套机制不断扩充自身的容量。
内存模型
vector发现自己的空间不够了,于是申请新的内存空间(自增一倍),并将前面已有数据复制到新空间的前部。
Comment
自增一倍,主要是“位移”运算。
对于vector增加新元素的时候,有可能很快完成,也有可能要进行扩容,效率下降;
删除末尾元素效率很高,删除中间元素效率低;
deque
双端队列
deque是双端队列,在接口上和vector非常相似,在许多操作的地方可以直接替换。
与vector不同的是,deque不能保证所有的元素存储在连续的空间中,在deque中通过指针加偏移量方式访问元素可能会导致非法的操作。
内存模型
Ref: C++ STL学习之三:容器deque深入学习(转)
除了在频繁在头部或尾部进行插入和删除操作外,deque比list和forward_list的性能更差。
deque是一种优化了的对序列两端元素进行添加和删除操作的基本序列容器。
通常由一些独立的区块组成,第一区块朝某方向扩展,最后一个区块朝另一方向扩展。
它允许较为快速地随机访问但它不像vector一样把所有对象保存在一个连续的内存块,而是多个连续的内存块。并且在一个映射结构中保存对这些块以及顺序的跟踪。
Ref: STL源码剖析---deque
deque采用一块所谓的map(注意,不是STL的map容器)作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体。
SGI STL 允许我们指定缓冲区大小,默认值0表示将使用512 bytes 缓冲区。
deque的迭代器
让我们思考一下,deque的迭代器应该具备什么结构,首先,
- 它必须能够指出分段连续空间(亦即缓冲区)在哪里;
- 其次它必须能够判断自己是否已经处于其所在缓冲区的边缘,如果是,一旦前进或后退就必须跳跃至下一个或上一个缓冲区。
为了能够正确跳跃,deque必须随时掌握管控中心(map)。所以在迭代器中需要定义:当前元素的指针,当前元素所在缓冲区的起始指针,当前元素所在缓冲区的尾指针,指向map中指向所在缓区地址的指针。
List
内核链表
From: 深入分析 Linux 内核链表
struct list_head {
struct list_head *next, *prev;
};
#define list_entry(ptr, type, member) container_of(ptr, type, member)
// container_of宏定义在[include/linux/kernel.h]中
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
// offsetof宏定义在[include/linux/stddef.h]中
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
内存模型
也就是普通的方式.
template <class T> struct __list_node { typedef void* void_pointer; void_pointer prev; void_pointer next; T data; };
Understand the cons and pros
数组 & 单链表
std::array
std::array
is just a class version of the classic C array. That means its size is fixed at compile time and it will be allocated as a single chunk (e.g. taking space on the stack). The advantage it has is slightly better performance because there is no indirection between the object and the arrayed data.
std::forward_list
"又快又小,但要在逻辑层面上:结构和需求相吻合."
forward_list 容器以单链表的形式存储元素。forward_list 的模板定义在头文件 forward_list 中。fdrward_list 和 list 最主要的区别是:它不能反向遍历元素;只能从头到尾遍历。
forward_list 的单向链接性也意味着它会有一些其他的特性:
- 无法使用反向迭代器。只能从它得到const或non-const前向迭代器,这些迭代器都不能解引用,只能自增;
- 没有可以返回最后一个元素引用的成员函数back();只有成员函数front();
- 因为只能通过自增前面元素的迭代器来到达序列的终点,所以push_back()、pop_back()、emplace_back()也无法使用。
forward_list 的操作比 list 容器还要快,而且占用的内存更少,尽管它在使用上有很多限制,但仅这一点也足以让我们满意了。
End.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律