C++ STL容器——序列式容器(array、vector、deque、list)

概述

1.C++ STL的容器分为三种,序列式容器,关联式容器,无序式容器,这里先说说常用的序列式容器。
2.array,vector,deque,list,forward_list这几种都是序列式容器,序列容器是以线性序列的方式存储元素,也就是说,在内存,是连续分配一块内存来存放元素的,是可以经过下标迭代的方式遍历元素。
3.C++的容器是不能用来装引用和函数的,容器的设计原则是以效率为先,安全为次的,所以容器本身并不带有异常处理。

一.基本用法

所有的序列式容器都有一些共同的属性和用法,这里我拿最学常用的std::vecot来举例子,这些属性array,vector,deque,list,forward_lis的使用方法是一样的。

  • 构造:
	using T = std::vector<float>;
	
	T a;
	T b(a);//拷贝构造
	T d = a;//拷贝构造,调用了operator =
	T e(std::move(b));//右值引用,执行完之后b里面为空值,但b所占的内存不变
	T c{ a };//C++ 11之后支持的拷贝方法
	T g(a.begin(), a.end());//区间拷贝
  • 属性:
	T t;
	int i = t.size();//返回容器大小,为了空间上的极致效率,forward_list没有这个属性
	bool tf = t.empty();//容器是否为空,等价于 t.size() != 0,但效率上要高一些
	auto max = t.max_size();//当前环境下最多还存放多少个该元素

	t.swap(a);//交换两个容器,但类型必须相同,实现是交换两个容器的指针(array除外)
	swap(t, a);//等价于上面
	t.begin();//返回容器头部迭代器位置
	t.end();//返回容器末尾部的迭代器合法位置的下一个位置,如果要解引用 *(t.end()-1)
	t.cbegin();//跟上面两不一样的是,这个返回的是const迭代器
	t.cend();
	t.rbegin();//反过来的迭代器
	t.rend();

	t.front();//返回头部元素的引用
	t.back();//返回尾部元素的引用

	t.clear();//清空容器,调用类的析构函数

二.std::array

1.概念
1.1 std::array是在C++11中才引入的容器,其实就是C++11对内置数组的重新封装,array对象在实例化的时候大小是固定的,并不支持动态添加或者删除元素,所以array默认是构造一个非空的实例。array的内存是分配在栈上的,对应的拷贝和赋值都要花掉一定的空间,绝不会重新分配内存。
1.2 在知道容器的大小情况,或者要与C进行交互时可以考虑使用std::array,毕竟array比内置的数组增加了一些常用的接口和可以对下标进行检查。
2. std::array的构造:

std::array<float, 100> a;//类型是float,有100个元素
std::array<float, 100> b = {};//全部按float的默认值初始化
std::array<float, 5> d = { 1.0f,33.0f,2.1f,4.3f,7.0f };//具体值初始化
std::array<float, 5> c = {2.0f};//第一个元素初始化为2.0,其余按类型默认值初始化

3.std::array的元素访问
遍历方法:

	//传统方法遍历
	for (size_t i = 0; i < d.size(); ++ i)
	{
		std::cout << d[i] << std::endl;//中括号是不检查下标是否合法
		std::cout << d.at(i) << std::endl;//.at()会检查下标,如果不合法则抛出异常
	}
	//迭代器遍历
	for (auto iter = d.begin(); iter != d.end(); ++iter)
	{
		std::cout << *iter << std::endl;//解引用
	}
	//auto 方式遍历
	for (auto v : d)
	{
		std::cout << v << std::endl;
	}
  1. 与C接口互用
	std::array<char, 10> c_str;
	strcpy(c_str.data(), "array char\n");//在vs中头文件要加#include <cstring>,项目属性/C++/预处理器加入:_CRT_SECURE_NO_WARNINGS
	printf("%s", c_str.data());

5.交换函数:

	t.swap(a);//交换两个容器,array是交换两个容器里面的值,而不是容器指针,这样容器一大效率就会降低
	swap(t, a);//等价于上面

三.std::vector

  1. 概念
    1.1 std::vector 是C++ 98就引入的动态数组,vector会在需要自动调整所占内存的大小,vector的这个特性让它比起静态数组所占用的内 存更多一些,因为它还分配了额外的内存以应对将来可能的内存扩张,在使用过程中,不用因为每次插入新的元素而重新分配内存,除非预留分配的那块内存用完,这样有效率会有很大的提升。
    1.2 std::vector的特点是能随机访问容器内的元素,在容器末端添加或者删除元素效率比较高,前端和中间则效率相对会低一些。
  2. 构造
	std::vector<float> d(10);//创建容器里面全部初始化为类型默认值(0.0)
	std::vector<float> e(10, 1.0f);// 创建容器里面全部初始化为1.0
	std::vector<float> c(e.begin(), e.end());//迭代器方式拷贝

	//C++ 11 
	std::vector<float> h({ 1.01, 1.1, 4.0, 3.0, 5.5 });
	std::vector<float> d{ 1.2, 3.7, 0.2, 10.4, 3.6 };
  1. 属性
	auto mun = h.capacity(); //返回容器当前能够容纳的元素数量
	h.reserve(10); //为容器预先分配内存,避免重新分配内存。
	h.assign(8, 1.1f);//压缩或者放大内存并指定默认值
	h.assign(e.begin(), e.end());//等价于 h = e
	h.assign({2.0,1.5,3.9});//压缩并指定初始化

	h.pop_back();//删掉最后一个元素,这里要确定容器不能为空
	h.push_back(11.12);//在容器尾部插入一个元素

	auto iterAfter =  h.erase(h.begin());//删掉指定元素,删掉后后面的元素要向前填补,
	                                     //返回要删除元素的下一个元素的引用,如果全部
										 //删完,则返回end(),使用该方法之后,迭代器有
										 //可能会失效(pop_back(),push_back()也有这个现象)
										 //因为vector有可能重新分配内存
	h.erase(h.begin(), h.end());//区间删掉元素,要确定区间值是闭合的


	auto iter = h.insert(h.end(),12.7f);//在指定位置插入一个新的元素,返回迭代器位置
	iter = h.insert(h.end(),10,12.1f);//在指定位置插入10元素,并初始化为指定值
	h.insert(h.end(), e.begin(), e.end());//在指定位置插入另一个容器区间

	h.resize(10);//缩小或者放大容器
	h.resize(20, 1.2f);//放大容器,如果之前不满20个,则插入的元素初始化为1.2,
					   //如果原本的容器大于20个,则忽略掉。

	h.clear();//清空掉容器里面所有的值,但容器的所占的内存不会变小

	h.shrink_to_fit();//请求容器降低其容量与当前所要存的元素匹配,
					  //但主动权在编译器,它决定是否真正释放多余的内存,
					  //这个方法只是提出请求,是否要实现由编译器说了算
					  //目前大部分的实现是内存能降下来
	t.swap(a);//交换两个容器,vector是交换两个容器的指针
	swap(t, a);//等价于上面
  1. 元素访问与容器遍历与array一样
	//传统方法遍历
	for (size_t i = 0; i < h.size(); ++i)
	{
		std::cout << h[i] << std::endl;//中括号是不检查下标是否合法
		std::cout << h.at(i) << std::endl;//.at()会检查下标,如果不合法则抛出异常
	}
	//迭代器遍历
	for (auto iter = h.begin(); iter != h.end(); ++iter)
	{
		std::cout << *iter << std::endl;//解引用
	}
	//auto 方式遍历
	for (auto v : h)
	{
		std::cout << v << std::endl;
	}
  1. 与C接口互用
	std::vector<char> c_str(20;
	strcpy(c_str.data(), "vector char\n");//在vs中头文件要加#include <cstring>,项目属性/C++/预处理器加入:_CRT_SECURE_NO_WARNINGS
	printf("%s", c_str.data());

四.std::deque

1.概念
1.1 std::deque是双端队列,头尾两端插入和删除元素比较高效,中间删除和添加元素效率低,在std::deque两端插入和删除并不会使其它元素的指针或引用失效。
1.2 元素的访问和迭代比vector慢,因为迭代器是智能指针而不是普通指针,内存分配不连续。
1.3 std::deque的存储空间会自动按需扩大和缩小,扩大std::deque比扩大std::vector的效率和开销会低很多,因为它不涉及到现有元素复制到新的内存位置。
2.定义方法和定义vecot的方法一样,没有不一样的地方。
3.属性
std::deque不提供 capacity(),reserve()这两个方法,因为deque分配的内存是一块一块的,不需要知道预分配多少内存。

	std::deque<float> d{ 1.2,3.7,0.2,10.4,3.6 };

	d.pop_back();//删除最后一个元素
	d.pop_front();//删除头元素

	;//在头部添加元素
	d.push_front(2.9f);
	d.emplace_front(3.7f);

	//在尾部添加元素
	d.push_back(3.3f);
	d.emplace_back(1.1f);

	d.emplace(d.end(),10.0f);//插入元素,指定初始化

4.元素访问与容器遍历与vector一样。
5.deque不能和C接口交互。
6.交换两个容器方法和效率跟vector一样。

五.std::list

1.概述
1.1 list是一个双向链表,不支持随机访问元素,访问头部和尾部元素速度快。
1.2 在list中任何位置进行插入/删除操作速度都很快,常量时间内完成,插入和删除不会造成迭代器失效。
1.3 对异常支持较好,出现异常对list而言,要么成功,要么什么影响都没有。
1.4 list在空间的成本上要比vector,deque高很多,因为list是一个双向列表,每个元素要有一个往前指针和往后的指针,list本身内部的元素是通过new的方式来生成的,比如用list存一个char元素时,在64位系统下,要开支33个字节左右(往前指针8个字节 + 往后指针8个字+new的16个字节 + char本身的一个字节)。

2.构造方法与vector一样。

3.属性。
list是一个个分配内存的,所以不提供capacity(),reserve(),shrink_to_fit() 这三个方法。

	std::list<float> d{ 1.2, 3.7, 0.2, 10.4, 3.6 };

	d.pop_back();//删除最后一个元素
	d.pop_front();//删除头元素

	//在头部添加元素
	d.push_front(2.9f);
	d.emplace_front(3.7f);

	//在尾部添加元素
	d.push_back(3.3f);
	d.emplace_back(1.1f);

	d.emplace(d.end(), 10.0f);//插入元素,指定初始化

4.元素访问与遍历
list不支持[]和at访问元素。

	//访问遍历元素
	auto iter_begin = d.begin();
	for (int i = 0; i < d.size()-1; ++i)
	{
		std::cout << *(++iter_begin) << std::endl;
	}

	auto iter = std::next(iter_begin,2);//访问第三个元素,返回迭代器引用
	std::cout << *iter << std::endl;

	//auto遍历元素
	for (auto v : d)
	{
		std::cout << v << std::endl;
	}

5.算法


	d.remove(1.2);//删除掉容器里面所有等于1.2的值
	d.remove_if([](auto v) {return v > 5.0f; });//条件删除
	d.reverse();//翻转整个容器
	d.sort();//排序,默认是从小到大

	//std::sort(d.begin(), d.end());//不能这么用

	h.sort();
	d.merge(h);//把两个排好序的容器合并成一个容器,之后h里面没有元素

	d.unique();//把两个排好序的容器里面重复的元素删掉
	d.splice(d.begin(),c);//把C容器安插到d的指定位置

5.list不能和C接口交互。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知来者逆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值