C++STL的容器的底层实现详解

文章目录

  • 顺序容器
    • vector(向量容器)
    • deque(双端队列)
    • list
  • 关联容器
    • set(集合)
    • multiset
    • map(key,value)
    • multimap

顺序容器

C++STL的容器的底层实现详解_第1张图片

vector(向量容器)

  • 特点
  • 内存可2倍增长的动态数组
  • 数据结构:线性连续空间
  • 维护三个迭代器:start、finish、end_of_storage
    C++STL的容器的底层实现详解_第2张图片

注意:动态增加大小,并不是在原空间之后接续新空间(因为无法保证之后尚有可供分配的空间),而是每次再分配原大小两倍的内存空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了。

  • 成员函数
函数 作用
assign(first,last) 用迭代器first,last所指定的元素取代容器中的元素
assign(num,val) 用val的num份副本取代2元素
at(n) 等价于[]运算符,返回容器中位置n的元素
front() 返回容器中第一个元素的引用
back() 返回容器中最后一个元素的引用
begin() 返回容器中第一个元素的迭代器
end() 返回容器中最后一个元素的下一个迭代器(不可解引用)
max_size() 返回容器类型的最大容量(2^30-1=0x3FFFFFFF)
capacity() 返回容器当前开辟的空间大小
size() 返回容器中现有元素的个数(<=capacity)
clear() 清空容器中所有元素
empty() 如果容器为空,返回真
erase(start,end) 删除迭代器[start ,end)所指定范围内的元素
erase(i) 删除迭代器i所指向的元素,返回指向删除元素下一位置的迭代器
insert(i,x) 把x插入到迭代器i所指定的位置之前
insert(i,n,x) 把x的n份副本插入到迭代器i所指定的位置之前
insert(i,start,end) 在i位置插入在[start,end)区间的数据,无返回值。
push_back(x) 尾插
op_back() 删除容器最后一个元素
rbegin() 返回一个反向迭代器,该迭代器指向的元素越过了容器中的最后一个元素
rend() 返回一个反向迭代器,该迭代器指向容器中第一个元素
reverse() 反转元素顺序
resize(n,x) 把容器的大小改为n,新元素的初值赋为x
swap(vector1) 交换2个容器的内容

deque(双端队列)

  • 特点

  • 数据结构:一种双向开口的存储空间分段连续的数据结构,每段数据空间内部是连续的,而每段数据空间之间则不一定连续

  • deque与vector的最大差异 :
    1.允许常数时间内对起首端进行元素的插入和删除
    2.deque没有所谓的容量概念,因为它是以分段连续空间组合而成,随时可以增加一段新的空间并连接起来
    C++STL的容器的底层实现详解_第3张图片
    上图deque有四部分数据空间,这些空间都是程序运行过程中在堆上动态分配的。
    中控器(map)保存着一组指针,每个指针指向一段数据空间的起始位置,通过中控器可以找到所有的数据空间。如果中控器的数据空间满了,会重新申请一块更大的空间,并将中控器的所有指针拷贝到新空间中。

  • deque采用一块所谓的map(注意,不是STL的map容器)作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体
    C++STL的容器的底层实现详解_第4张图片

  • deque 的迭代器

deque的迭代器由四个属性组成,这四个属性是我们随机访问一个容器内元素的必要成分。

1.node用于指向的“第一维”的某个位置,该位置对应要访问元素的入口地址
2.剩下的三个属性则用于“第二维”,[first,last)规定了访问本区间元素的边界条件,而curr则指向实际我们要访问的元素。

C++STL的容器的底层实现详解_第5张图片
1.start迭代器:绑定到第一个有效的map结点和该结点对应的缓冲区。

2.finish迭代器:绑定到最后一个有效的map结点和该结点对应的缓冲区。

举例:
假设deque中存储20个元素,每个缓冲区大小为8,则需要20/8=3个缓冲区,所以map中会运用3个节点。deque的begin()和end()始终会返回map中节点的头和尾,名为start和finish,其中,start的cur指向第一个缓冲区中的首元素,finish的cur指向最后一个缓冲区的最后一个元素的下一位置。
C++STL的容器的底层实现详解_第6张图片

  • deque缓冲区扩充

C++STL的容器的底层实现详解_第7张图片
C++STL的容器的底层实现详解_第8张图片
C++STL的容器的底层实现详解_第9张图片
C++STL的容器的底层实现详解_第10张图片
注意:从上图来看,如果无穷尽的向deque中存储数据,map所含有的缓冲区也会填满的。也就是说,当map的前端节点或后端节点备用空间不足时,map也需要重新配置,依然是经过三个步骤:配置更大的、拷贝原来的、释放原空间。

  • 成员函数
函数 作用
c.assign(beg,end) 将[beg; end)区间中的数据赋值给c。
c.assign(n,elem) 将n个elem的拷贝赋值给c。
c.at(idx) 传回索引idx所指的数据,如果idx越界,抛出out_of_range。
c.back() 返回最后一个数据,不检查这个数据是否存在。
c.begin() 返回迭代器的第一个数据。
c.clear() 移除容器中所有数据。
deque 创建一个空的deque。
deque c1(c2) c复制一个deque。
deque c(n) 创建一个deque,含有n个数据,数据均已缺省构造产生。
deque c(n, elem) 创建一个含有n个elem拷贝的deque。
deque c(beg,end) 创建一个以[beg;end)区间的deque。
c.~deque() 销毁所有数据,释放内存。
c.empty() 判断容器是否为空。
c.end() 指向迭代器中的最后一个数据地址。
c.erase(pos) 删除pos位置的数据,返回下一个数据的位置。
c.erase(beg,end) 删除[beg,end)区间的数据,返回下一个数据的位置。
c.front() 返回容器的第一个元素。
get_allocator 使用构造函数返回一个拷贝。
c.insert(pos,elem) 在pos位置插入一个elem拷贝,传回新数据位置。
c.insert(pos,n,elem) 在pos位置插入>n个elem数据。无返回值。
c.insert(pos,beg,end) 在pos位置插入在[beg,end)区间的数据。无返回值。
c.max_size() 返回容器中最大数据的数量。
c.pop_back() 删除最后一个数据。
c.pop_front() 删除头部数据。
c.push_back(elem) 在尾部加入一个数据。
c.push_front(elem) 在头部插入一个数据。
c.rbegin() 传回一个反向队列的第一个数据。
c.rend() 传回一个反向队列的最后一个数据的下一个位置。
c.resize(num) 重新指定队列的长度。
c.size() 返回容器中实际数据的个数。
c1.swap(c2) 将c1和c2元素互换
swap(c1,c2) 将c1和c2元素互换。

list

  • 特点

  • 有效利用空间

  • 数据结构:环状双向链表

  • 插入(insert)和接合(splice)操作都不会造成原来list的迭代器失效

  • 删除(erase)操作仅仅使“指向被删除元素”的迭代器失效,其它迭代器不受影响

  • 随机访问比较慢

C++STL的容器的底层实现详解_第11张图片
C++STL的容器的底层实现详解_第12张图片
插入一个结点
C++STL的容器的底层实现详解_第13张图片

  • 成员函数
函数 作用
assign() 给list赋值
back() 返回最后一个元素
begin() 返回指向第一个元素的迭代器
clear() 删除所有元素
empty() 如果list是空的则返回true
end() 返回末尾的迭代器
erase() 删除一个元素
front() 返回第一个元素
get_allocator() 返回list的配置器
insert() 插入一个元素到list中
max_size() 返回list能容纳的最大元素数量
merge() 合并两个list
pop_back() 删除最后一个元素
pop_front() 删除第一个元素
push_back() 在list的末尾添加一个元素
push_front() 在list的头部添加一个元素
rbegin() 返回指向第一个元素的逆向迭代器
remove() 从list删除元素
remove_if() 按指定条件删除元素
rend() 指向list末尾的逆向迭代器
resize() 改变list的大小
reverse() 把list的元素逆序
size() 返回list中的元素个数
sort() 给list排序
splice() 合并两个list
swap() 交换两个list
unique() 删除list中重复的元素

关联容器

set(集合)

  • 特点
  • 数据结构:底层使用平衡的搜索树——红黑树实现
    C++STL的容器的底层实现详解_第14张图片
  • 插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝
  • set中元素都是唯一的,而且默认情况下会对元素自动进行升序排列
  • 支持集合的交(set_intersection),差(set_difference) 并(set_union),对称差 (set_symmetric_difference) 等一些集合上的操作

C++STL的容器的底层实现详解_第15张图片

  • 成员函数
函数 作用
begin() 返回容器的第一个迭代器
end() 返回容器的最后元素的下一个迭代器
clear() 删除容器中的所有元素
empty() 判断容器是否为空
insert() 插入一个元素
erase() 删除一个元素
size() 返回当前容器的元素个数
rbegin() 返回尾元素的逆向迭代器指针
reverse_iterator rend() 返回首元素前一个位置的迭代器指针
find() 查找一个元素,不存在返回s.end()
lower_bound() 返回第一个大于或等于给定关键值的元素
upper_bound() 返回第一个大于给定关键值的元素
swap() 交换两个集合元素

注意:

  • set内部元素也是以键值对的方式存储的,只不过它的键值与实值相同
  • set中不允许存放两个实值相同的元素
  • 迭代器是被定义成const iterator的,说明set的键值是不允许更改的,并且不允许通过迭代器进行修改set里面的值

multiset

  • 特点
  • 数据结构:底层实现与set一样,也采用了红黑树
  • 允许插入重复的键值,使用insert_equal机制
  • 插入、删除操作的时间复杂度为O(log2n)
    C++STL的容器的底层实现详解_第16张图片
  • 成员函数用法同set

map(key,value)

  • 特点

  • map中key的值是唯一的

  • 数据结构:红黑树变体的平衡二叉树数据结构

  • 提供基于key的快速检索能力

  • 元素插入是按照排序规则插入的,不能指定位置插入

  • 对于迭代器来说,可以修改实值,而不能修改key。

  • 根据key值快速查找,查找的复杂度基本是log2n

  • 成员函数

函数 作用
begin() 返回指向map头部的迭代器
clear() 删除所有的元素
count() 返回指定元素出现的次数
empty() 判断容器是否为空
end() 返回指向map末尾的迭代器
get_allocator() 返回map的配置器
insert() 添加元素
lower_bound() 返回键值>=给定元素的第一个位置
upper_bound() 返回>给定元素的第一个元素
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
find(k) 返回指向第一个与键 k 匹配的 pair 的迭代指针,没找到返回指向map尾部的迭代器
erase() 删除迭代器所指向的元素
swap() 两个容器交换
size() 返回map中元素的个数

注意:
map在进行插入的时候是不允许有重复的键值的,如果新插入的键值与原有的键值重复则插入无效,可以通过insert的返回的pair中第二个bool型变量来判断是否插入成功来判断是否成功插入.

pair insert(const value_type& x)

举例:

#include<iostream>
#include<map>
using namespace std;
int main()
{
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	pair<map<int, int>::iterator, bool> ret;
	ret = m.insert(pair<int, int>(1, 20));
	if (ret.second)
	{
		cout << "成功" << endl;
	}
	else
	{
		cout << "失败" << endl;
	}
	

	return 0;
}

在这里插入图片描述

multimap

  • 特点
  • 与 map 不同,multimap 可以包含重复键

比如:

1.在电话簿中相同的人可以有两个以上电话号码
2.文件系统中可以将多个符号链接映射到相同的物理文件
3.DNS服务器可以将几个URL映射到相同的IP地址
  • 成员函数
函数 作用
begin() 返回指向第一个元素的迭代器
clear() 删除所有元素
count() 返回一个元素出现的次数
empty() 如果multimap为空则返回真
end() 返回一个指向multimap末尾的迭代器
equal_range(k) 该函数查找所有与 k 关联的值。返回迭代指针的 pair,它标记开始和结束范围
erase() 删除元素
find() 查找元素
get_allocator() 返回multimap的配置器
insert() 插入元素
key_comp() 返回比较key的函数
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向mulitmap尾部的逆向迭代器
rend() 返回一个指向multimap头部的逆向迭代器
size() 返回multimap中元素的个数
swap() 交换两个multimaps
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素value的函数

equal_range()函数应用举例:

#include<iostream>
#include<map>
using namespace std;

int main()
{
	multimap<int, int> m;
	m.insert(pair<int,int>(1, 10));
	m.insert(pair<int, int>(1, 20));
	m.insert(pair<int, int>(1, 30));
	m.insert(pair<int, int>(2, 21));
	m.insert(pair<int, int>(2, 22));
	m.insert(pair<int, int>(3, 31));
	m.insert(pair<int, int>(3, 32));

	multimap<int, int>::iterator  it;
	pair<multimap<int, int>::iterator, multimap<int, int>::iterator> ret;
	for (it = m.begin(); it != m.end(); )
	{
		cout << it->first << "==>";
		ret = m.equal_range(it->first);
		for (it = ret.first; it != ret.second; ++it)
		{
			cout << " " << (*it).second;
		}
		cout << endl;
	}
	
	cout << m.count(1) << endl;//与键1关联的值的数量
	return 0;
}

C++STL的容器的底层实现详解_第17张图片

注意:

  1. multimap::insert() 和 map::insert() 的返回值不同。
  2. multimap::insert():返回指向新插入元素的迭代器(multimap::insert()总是能执行成功)
  3. map::insert() :返回 pair,此处 bool 值表示插入操作是否成功。

你可能感兴趣的:(C++)