【C++】序列与关联容器(二)序列容器
- 一、C++ 标准库中的序列容器模板
- 二、array 容器模板
- 三、vector元素模板
一、C++ 标准库中的序列容器模板
每种容器的实现方法的难易不同,特性不同,适用的应用场景不同
- array :元素个数固定的序列容器(不支持对象的增添删除)
- vector :元素连续存储的序列容器(每一个数据的地址完全连续,在一个连续的空间内存储)
- forward_list / list :基于链表 / 双向链表的容器(每一个数据都不连续,通过内部指针连接在一起)
- deque :vector 与list 的折衷(有多段,每一段之间通过指针联系在一起,段内是一段连续的内存空间)
- basic_string :提供了对字符串专门的支持
上述容器都是模板,都需要使用元素类型来实例化容器模板,从而构造可以保存具体类型的容器。
不同的容器所提供的接口大致相同,但根据容器性质的差异,其内部实现与复杂度不同。
对于复杂度过高的操作,提供相对较难使用的接口或者不提供相应的接口
二、array 容器模板
具有固定长度的容器,其内部维护了一个内建数组,与内建数组相比提供了复制操作
std::array<int,3> x={};
std::array<int,3> y=x;//支持数组复制操作
由于array是固定长度容器,不支持后期删除或者增添元素,所以必须要在定义的时候就给出长度参数。
提供的接口:
- 构造
缺省初始化:可能会导致数组中的值是乱的。一般不建议这样做。
std::array<int,3> x;
赋值初始化:
std::array<int,3> x={3};
- 元素访问:[],at,front,back,data
std::array<int, 3> x = {1,2,3};
std::cout << x[2] << std::endl; //3
std::cout << x.at(2) << std::endl; //3
std::cout << x.front() << std::endl; //打印首个元素 1
std::cout << x.back() << std::endl; //打印最后一个元素 3
std::cout << x.data()<< std::endl; //.data操作返回的是 T*,也就是该数组的首地址。这个和智能指针的get()用法是一个意思。由于智能指针以及array数组都不是普通的数据类型,而是一个模板类,所以其本身不是一个指针,需要用特定的命令获取纯的数据类型指针,就要用到这样的命令。
关于上面程序代码最后一行提到的data命令:array,vector这些都有,基本上如果是连续保存的数据类型就会有,而分散保存的,比如list就不会有。
- 容量相关(平凡实现):empty,size
std::array<int, 3> x = {1,2,3};
std::cout << x.empty() << std::endl; //输出0,因为不是empty
std::cout << x.size() << std::endl; //3
std::array<int, 0> y ;
std::cout << y.empty() << std::endl; //输出1,因为是empty
- 填充与交换:fill,swap
std::array<int, 3> x ;
x.fill(100); //把x中所有元素全部赋值为100
std::array<int, 3> x={1,2,3} ;
std::array<int, 3> y={4,5,6} ;
x.swap(y); //把x与y中的元素交换,此时x={4,5,6},y={1,2,3}.
- 比较操作:<=>
比较数组内元素的每一个值的大小。 - 迭代器
三、vector元素模板
vector 容器模板:元素可变
提供的接口:与array 很类似,但有其特殊性
- 容量相关接口:capacity / reserve / shrink_to_fit
reserve的用法: 像下面这样给一个vector赋值,是没有问题的,但是系统性能太慢。原因是vector是动态分配内存,这样会经常出现已经给vector分配的内存满了,然后需要分配新的更大的内存,再拷贝过去,在新的内存上增添数据这样的情况。
std::vector<int> x;
for (int i = 0; i < 1024; ++i)
{
x.push_back(i);
}
所以引入reserve用法:
使用reserve 的情况仅限于已知要给vector存入多少数据的情况。
在这种情况下,在给vector赋值对象数据之前,先调用x.reserve(1024);
,此处的1024就是你想给vector存多少数据的那个值,他就会自动先分配1024个数据大小的内存空间,则后面赋值的时候就不会出现内存不够,分配新内存,再拷贝的情况,大大节省了系统性能,也提高了运行速度。
std::vector<int> x;
x.reserve(1024);
for (int i = 0; i < 1024; ++i)
{
x.push_back(i);
}
shrink_to_fit用法:
还是由于vector是动态分配内存,且前文提到过,系统的机制是存一个数他会开辟两个数据大小的内存空间。所以常会出现已经存完数据了,但是系统给vector分配的内存空间还没有用完,就像前文的图所示。这时用到shrink_to_fit用法,传入的参数是该vector的实际大小,使得系统在其他地方重新开辟一块这个大小的内存空间,把数据拷贝过去,把原来的内存空间释放掉。该命令是为了节省内存空间。
- 附加元素接口:push_back / emplace_back
emplace_back在某些情况下会比push_back效率更高,性能更好(具体是在string类型的vector时可能更好) - 元素插入接口:insert / emplace
- 元素删除接口:pop_back(弹出最后一个) / erase(弹出某一个) / clear (清楚全部)
注意:
- vector 不提供push_front / pop_front ,可以使用insert / erase 模拟,但效率不高
- 与array相比,vector的swap 效率较高
- 写操作可能会导致迭代器失效