C++ 顺序容器(vector、list、deque、array、forward_list)详解

下一篇:C++ 关联容器(map、multimap 、set、multiset)详解

一、容器的定义

在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对像的指针,这种对象类型就叫做容器。很简单,容器就是保存其它对象的对象,当然这是一个朴素的理解,这种“对象”还包含了一系列处理“其它对象”的方法。

二、容器的种类

1. 顺序容器:

是一种各元素之间有顺序关系的线性表,是一种线性结构的可序群集。顺序性容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。顺序容器包括:vector(向量)、list(列表)、deque(队列)、array(数组)、forward_list。

2. 关联容器:

关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。但是关联式容器提供了另一种根据元素特点排序的功能,这样迭代器就能根据元素的特点“顺序地”获取元素。元素是有序的集合,默认在插入的时候按升序排列。关联容器包括:map(集合)、set(映射)、multimap(多重集合)、multiset(多重映射)。

3.容器适配器:

本质上,适配器是使一种不同的行为类似于另一事物的行为的一种机制。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。适配器是容器的接口,它本身不能直接保存元素,它保存元素的机制是调用另一种顺序容器去实现,即可以把适配器看作“它保存一个容器,这个容器再保存所有元素”。STL 中包含三种适配器:栈stack 、队列queue 和优先级队列priority_queue 。
容器类自动申请和释放内存,因此无需new和delete操作。
C++ 顺序容器(vector、list、deque、array、forward_list)详解_第1张图片

三、不同顺序容器使用方法

1、array(数组)

是个顺序容器,其实是个数组,内存空间连续,大小固定。刚开始申请内存多大,就是多大,不能再增加其容量。

1、详细用法

参考博客:c++ array模板类使用

2、vector(需要导入头文件#include )

1. 定义与初始化

如果没有指定元素的初始化式,那么标准库将自行提供一个元素初始值进行,具体值为何,取决于存储在vector 中元素的数据类型;如果为int型数据,那么标准库将用 0 值创建元素初始化式;如果 vector 保存的是含有构造函数的类类型(如 string)的元素,标准库将用该类型的默认构造函数创建元素初始化式;元素类型可能是没有定义任何构造函数的类类型。这种情况下,标准库仍产生一个带初始值的对象,这个对象的每个成员进行了值初始化。

    vector<int> vec1;    //默认初始化,vec1为空
    vector<int> vec2(vec1);  //使用vec1初始化vec2
    vector<int> vec3(vec1.begin(),vec1.end());//使用vec1初始化vec2
    vector<int> vec4(10);    //10个值为0的元素
    vector<int> vec5(10,4);  //10个值为4的元素
    vector<string> vec6(10,"null");    //10个值为null的元素
    vector<string> vec7(10,"hello");  //10个值为hello的元素
2. 常用的操作方法
    vec1.push_back(100);            //末尾添加元素
    int size = vec1.size();         //元素个数
    bool isEmpty = vec1.empty();    //判断是否为空
    cout<<vec1[0]<<endl;        //取得第一个元素
    vec1.insert(vec1.end(),5,3);    //从vec1.back位置插入5个值为3的元素
    vec1.pop_back();              //删除末尾元素
    vec1.erase(vec1.begin(),vec1.end());//删除之间的元素,其他元素前移(返回指向下一个元素的迭代器)
    //元素个数相同,且对应的元素值也要相同
    cout<<(vec1==vec2)?true:false;  //判断是否相等==、!=、>=、<=...
    vector<int>::iterator iter = vec1.begin();    //获取迭代器首地址
    vector<int>::const_iterator c_iter = vec1.begin();   //获取const类型迭代器
    vector<int>::reverse_iterator iter = vec.rbegin();//获取一个反向迭代器
    vec1.clear();                 //清空元素
    vec1.front();    //获取第一个元素
    vec1.back();    //获取最后一个元素
    vec1.max_size();    //向量最大容量
    vec1.resize();    //更改向量大小
    vec1.capacity();   //向量真实大小
    vec1.swap();   //交换两个向量的元素 

注意:

  1. vector::iterator iter = vec.end();迭代器指针时指向最后一个元素的下一个位置,故不能通过迭代器指针直接访问,直接访问会造成程序崩溃。
  2. C++11引入的两个新函数 cbegin(),cend()。和 begin(),end()类似,但是返回的是常量迭代器。
3. 遍历方法
  1. 下标法(vector的特有访问方法,一般容器只能通过迭代器访问)
  	int length = vec1.size();
    for(int i=0;i<length;i++)
    {
       cout<<vec1[i];
    }
    cout<<endl<<endl;
  1. 迭代器法
    vector::iterator iter = vec.begin(); //正向迭代器
    vector::reverse_iterator iter = vec.begin(); //反向迭代器
    vector::const_iterator iter = vec.begin(); //const 迭代器

注意:如果容器用const修饰,则迭代器访问时,必须使用 const 迭代器

    vector<int>::const_iterator iterator = vec1.begin();	//const 迭代器,表示不能通过该迭代器指针修改内容。
    for(;iterator != vec1.end();iterator++)
    {
       cout<<*iterator;
    }

	vector<Student> vec;
	Student stu;
	stu.name = "小王";
	stu.age = 25;
	vec.push_back(stu);
	if (!vec.empty())
	{
		vector<Student>::iterator iter = vec.begin();
		cout << (*iter).name << endl;	//输出首元素对象的名字
		cout << iter->name << endl;	//注意此用法
		cout << vec.front().name << endl;
	}

防止迭代器失效的方法:

	vector<int> vec{ 10,15,25,56 };
	auto iter = vec.begin();
	int count = 0;
	while (iter != vec.end())	//每次更新end,防止迭代器失效
	{
		iter = vec.insert(iter, 85);
		count++;

		if (count > 10)
		{
			break;
		}
		iter++;
	}

	for (const auto& it : vec)
	{
		cout << it << endl;
	}
	vector<int> vec{ 10,15,25,56 };
	auto iter = vec.begin();
	while (iter != vec.end())
	{
		iter = vec.erase(iter);	//移除iter位置上的元素,返回下一个元素的位置
	}

	vector<int> vec{ 10,15,25,56 };
	while (!vec.empty())
	{
		auto iter = vec.begin();	//移除iter位置上的元素,返回下一个元素的位置
		vec.erase(iter);
	}

3、list(需要导入头文件#include )

注意:双向链表,不需要各个元素之间的内存连续,查找效率不突出,但是能快速的插入和删除。

1. 定义与初始化
    list<int> lst1;          //创建空list
    list<int> lst2(3);       //创建含有三个元素的list
    list<int> lst3(3,2); //创建含有三个元素的值为2的list
    list<int> lst4(lst2);    //使用lst2初始化lst4
    list<int> lst5(lst2.begin(),lst2.end());  //同lst4
2.常用的操作方法
    lst1.assign(lst2.begin(),lst2.end());  //分配值
    lst1.push_back(10);                    //添加值
    lst1.pop_back();                   //删除末尾值
    lst1.begin();                      //返回首值的迭代器
    lst1.end();                            //返回尾值的迭代器
    lst1.clear();                      //清空值
    bool isEmpty1 = lst1.empty();          //判断为空
    lst1.erase(lst1.begin(),lst1.end());                        //删除元素
    lst1.front();                      //返回第一个元素的引用
    lst1.back();                       //返回最后一个元素的引用
    lst1.insert(lst1.begin(),3,2);         //从指定位置插入3个值为2的元素
    lst1.rbegin();                         //返回第一个元素的前向指针
    lst1.remove(2);                        //相同的元素全部删除
    lst1.reverse();                        //反转
    lst1.size();                       //含有元素个数
    lst1.sort();                       //排序
    lst1.unique();                         //删除相邻重复元素
3. 遍历方法
  1. 迭代器法
    for(list<int>::const_iterator iter = lst1.begin();iter != lst1.end();iter++)
    {
       cout<<*iter;
    }
    cout<<endl;

4、deque(双端队列)

(需要导入头文件#include )
deque容器类与vector类似,支持随机访问和快速插入删除,它在容器中某一位置上的操作所花费的是线性时间。与vector不同的是,deque还支持从开始端插入数据:push_front()。其余类似vector操作方法的使用。

注意:它允许较为快速地随机访问但它不像vector一样把所有对象保存在一个连续的内存块,而是多个连续的内存块

详细使用:参考博客C++deque介绍

5、stack(栈容器)

详细用法:C++ stack(STL stack)用法详解

6、queue(普通队列)

先进先出,比较基本的数据结构。
详细使用:参考博客:C++ queue(STL queue)用法详解

7、forward_list(单向链表)

C++11新增的,单向链表(受限list),但节省了内存
详细使用:参考博客:C++ forward_list用法详解

8、顺序容器使用方法小结(操作的共同点)

  1. 添加元素
函数名 意义
c.push_back(t) 在容器c的尾部添加值为t的元素。返回void 类型
c.push_front(t) 在容器c的前端添加值为t的元素。返回void 类型,只适用于list和deque容器类型。
c.insert(p,t) 在迭代器p所指向的元素前面插入值为t的新元素。返回指向新添加元素的迭代器。
c.insert(p,n,t) 在迭代器p所指向的元素前面插入n个值为t的新元素。返回void 类型
c.insert(p,b,e) 在迭代器p所指向的元素前面插入由迭代器b和e标记的范围内的元素。返回 void 类型
//在容器首部或者尾部添加数据
list<int> ilist;
ilist.push_back(ix);//尾部添加
ilist.push_front(ix);//首部添加
//在容器中指定位置添加元素
list<string> lst;
list<string>::iterator iter = lst.begin();
while (cin >> word)
iter = lst.insert(iter, word); // 和push_front意义一样
//插入一段元素

list<string> slist;
string sarray[4] = {"quasi", "simba", "frollo", "scar"};
slist.insert(slist.end(), 10, "A");//尾部前添加十个元素都是A
list<string>::iterator slist_iter = slist.begin();
slist.insert(slist_iter, sarray+2, sarray+4);//指针范围添加
  1. 器大小的操作
函数名 意义
c.size() 返回容器c中元素个数。返回类型为 c::size_type
c.max_size() 返回容器c可容纳的最多元素个数,返回类型为c::size_type
c.empty() 返回标记容器大小是否为0的布尔值
c.resize(n) 调整容器c的长度大小,使其能容纳n个元素,如果n
c.resize(n,t) 调整容器c的长度大小,使其能容纳n个元素。所有新添加的元素值都为t
list<int> ilist(10, 1);
ilist.resize(15); // 尾部添加五个元素,值都为0
ilist.resize(25, -1); // 再在尾部添加十个元素,元素为-1
ilist.resize(5); // 从尾部删除20个元素
  1. 访问元素
函数名 意义
c.back() 返回容器 c 的最后一个元素的引用。如果 c 为空,则该操作未定
c.front() 返回容器 c 的第一个元素的引用。如果 c 为空,则该操作未定义
c[n] 返回下标为 n 的元素的引用。如果 n <0 或 n >= c.size(),则该操作未定义,只适用于 vector 和 deque 容器
c.at(n) 返回下标为 n 的元素的引用。如果下标越界,则该操作未定义,只适用于 vector 和 deque 容器
vector<int> vi;
for(int i=0;i<10;i++)vi.push_back(i);
cout<<vi[0]<<endl;
cout<<vi.at(0)<<endl;
cout<<vi[10]<<endl; //越界错误
cout<<vi.at(10)<<endl;//越界错误
  1. 删除元素
函数名 意义
c.erase§ 删除迭代器p所指向的元素。返回一个迭代器,它指向被删除元素后面的元素。如果p指向容器内的最后一个元素,则返回的迭代器指向容器超出末端的下一位置。如果p本身就是指向超出末端的下一位置的迭代器,则该函数未定义
c.erase(b,e) 删除迭代器b和e所标记的范围内所有的元素。返回一个迭代器,它指向被删除元素段后面的元素。如果e本身就是指向超出末端的下一位置的迭代器,则返回的迭代器也指向容器的超出末端的下一位置
c.clear() 删除容器c内的所有元素。返回void
c.pop_back() 删除容器c的最后一个元素。返回void。如果c为空容器,则该函数未定义
c.pop_front() 删除容器c的第一个元素。返回void。如果c为空容器,则该函数未定义,只适用于 list 或 deque 容器
//删除第一个或最后一个元素
list<int> li;
for(int i=0;i<10;i++)list.push_back(i);
li.pop_front();//删除第一个元素
li.pop_back(); //删除最后一个元素
//删除容器内的一个元素
list<int>::iterator iter =li.begin();
if(iter!= li.end())li.erase(iter);
//删除容器内所有元素
li.clear();
  1. 赋值与swap
函数名 意义
c1 = c2 删除容器c1的所有元素,然后将c2的元素复制给c1。c1和c2的类型(包括容器类型和元素类型)必须相同
c1.swap(c2) 交换内容:调用完该函数后,c1中存放的是 c2 原来的元素,c2中存放的则是c1原来的元素。c1和c2的类型必须相同。该函数的执行速度通常要比将c2复制到c1的操作快
c.assign(b,e) 重新设置c的元素:将迭代器b和e标记的范围内所有的元素复制到c中。b和e必须不是指向c中元素的迭代器
c.assign(n,t) 将容器c重新设置为存储n个值为t的元素
list<string> sl1,sl2;
for(int i=0;i<10;i++)sl2.push_back("a");
sl1.assign(sl2.begin(),sl2.end());//用sl2的指针范围赋值,sl1中十个元素都为a
sl1.assign(10, "A"); //s1被重新赋值,拥有十个元素,都为A
vector<string> vs1(3); // vs1有3个元素
vector<string> vs(5); // vs2有5个元素
vs1.swap(vs2);//执行后,vs1中5个元素,而vs2则存3个元素。

注意:vector和list的区别
1. vector类似于数组,内存空间是连续的;list是双向链表,内存空间并不连续。
2. vector从中间或者开头插入元素效率比较低;但是list插入元素效率非常高。
3. vector当内存不够时,会动态分配内存,对原来的对象做析构,在新栈的内存中重新构建对象。
4. vector能够高效的随机存取,而list随机存取比较慢。

你可能感兴趣的:(C++基础/高级,列表,c++,容器,stl)