C++总结(5):STL顺序容器之array、vector、deque、forward_list、list详解

容器是一个持有其他对象元素集合的容器对象,以类模板的形式实现,可以灵活地支持不同类型的元素。容器负责管理其元素的存储空间,并提供成员函数来直接或通过迭代器(类似于指针的引用对象)访问它们。

C++中有多种种类的容器,包括:顺序容器(Sequence Containers)、关联容器(Associative Containers)、无序容器(Unordered Containers)和容器适配器(Container Adapters)。

这篇文章先来详细介绍一下顺序容器的用法。顺序容器实现了可以按顺序访问的数据结构,它包括array(静态连续数组)、vector(动态连续数组)、deque(双端队列)、forward_list(单向链表)和list(双向链表)。

1 array

C++11中的array类的引入为C风格数组提供了更好的替代方案。array类相对于C数组的优势在于:

  1. array类知道自己的大小,因此在传递给函数时,不需要将数组的大小作为单独的参数传递
  2. C数组在作为参数传递时有可能退化为指针,而array类不会退化为指针

array类的操作

1.访问数组元素at()/get()/[]

#include<iostream>
#include<array> // for array, at()
#include<tuple> // for get()
using namespace std;
int main()
{
	array<int,6> ar = {1, 2, 3};

	// Printing array elements using at()
	cout << "The array elements are (using at()) : ";
	for ( int i=0; i<3; i++)
	    cout << ar.at(i) << " ";
	cout << endl;

	cout << "The array elements are (using get()) : ";
	cout << get<0>(ar) << " " << get<1>(ar) << " " << get<1>(ar) << endl;

	cout << "The array elements are (using operator[]) : ";
	for ( int i=0; i<3; i++)
	    cout << ar[i] << " ";
	cout << endl;

	return 0;
}

2. front()/back() :返回array中的第一个/最后一个元素的引用

array<int,6> ar = {1, 2, 3, 4, 5, 6};

int &a=ar.front();
cout << "First element of array is : " << a << endl; //输出1

int &b=ar.back();
cout << "Last element of array is : " << b << endl;  //输出6

3.size()/max_size():返回array的元素个数/返回数组声明时的大小

array<int,6> ar = {1, 2, 3, 4, 5, 6};
cout << "The number of array elements is : " << ar.size() << endl;  //返回6
cout << "Maximum elements array can hold is : " << ar.max_size() << endl;  //返回6

4.swap():交换两个array的元素

array<int,6> ar = {1, 2, 3, 4, 5, 6};
array<int,6> ar1 = {7, 8, 9, 10, 11, 12};
ar.swap(ar1);  //此时ar和ar1的数组调换

5.empty():如果array元素个数为0返回真

6.fill():用某个值填充整个array

array<int,6> ar;
array<int,0> ar1;
ar1.empty()? cout << "Array empty" << endl:cout << "Array not empty" << endl;//empty
ar.fill(0);//ar的六个元素均为0

2 vector

vector在插入或删除元素时,它们能够自动调整大小。元素被放置在连续的存储空间中,以便可以使用迭代器访问和遍历它们。插入元素的所需的时间不同,因为需要找一个更大的内存以扩展数组大小。而删除元素的时间则是确定的,因为无需寻找新的内存。

1.迭代器(iterators)

  • begin():返回指向vector中第一个元素的迭代器
  • end():返回指向vector中最后一个元素之后的理论元素的迭代器
  • rbegin():返回指向vector中最后一个元素(反向开始)的反向迭代器,它从最后一个元素往第一个元素移动
  • rend():返回指向vector中第一个元素之前的理论元素(视为反向结束)的反向迭代器
  • cbegin():返回指向vector中第一个元素的常量迭代器
  • cend():返回指向vector中最后一个元素之后的理论元素的常量迭代器
  • crbegin():返回指向vector中最后一个元素(反向开始)的常量反向迭代器,它从最后一个元素往第一个元素移动
  • crend():返回指向vector中第一个元素之前的理论元素(视为反向结束)的常量反向迭代器。

注意:这里的常量迭代器指的是不允许使用迭代器修改元素的值。下面来看一下例子:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> g1;

	for (int i = 1; i <= 5; i++)
		g1.push_back(i);
	
	cout << "Output of begin and end: ";  //1 2 3 4 5
	for (auto i = g1.begin(); i != g1.end(); ++i)
		cout << *i << " ";
	
	cout << "\nOutput of cbegin and cend: ";  //1 2 3 4 5
	for (auto i = g1.cbegin(); i != g1.cend(); ++i)
		cout << *i << " ";
	
	cout << "\nOutput of rbegin and rend: ";  //5 4 3 2 1
	for (auto ir = g1.rbegin(); ir != g1.rend(); ++ir)
		cout << *ir << " ";
	
	cout << "\nOutput of crbegin and crend : ";  //5 4 3 2 1
	for (auto ir = g1.crbegin(); ir != g1.crend(); ++ir)
		cout << *ir << " ";
	return 0;
}

2.容量(capacity)

  • size():返回vector中的元素数量
  • max_size():返回vector可以容纳的最大元素数量(根据内存返回)
  • capacity():返回目前分配给vector的存储空间大小,以元素数量表示
  • resize(n):调整vector的大小,使其包含’n’个元素
  • empty():返回vector是否为空
  • shrink_to_fit():将vector的容量减少到适应其大小,并销毁超出容量的所有元素
  • reserve():要求vector容量至少足以容纳n个元素
#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> g1;

	for (int i = 1; i <= 5; i++)
		g1.push_back(i);

	cout << "Size : " << g1.size(); //5
	cout << "\nCapacity : " << g1.capacity();  //8(说明默认分配8字节,8字节对齐?)
	cout << "\nMax_Size : " << g1.max_size();  //根据实际内存返回


	g1.resize(4);
	cout << "\nSize : " << g1.size(); //4


	if (g1.empty() == false)
		cout << "\nVector is not empty";  //√
	else
		cout << "\nVector is empty";

	g1.shrink_to_fit();
	cout << "\nVector elements are: ";  //1 2 3 4
	for (auto it = g1.begin(); it != g1.end(); it++)
		cout << *it << " ";

	return 0;
}

3.访问元素

  • [n]:返回vector中第n个元素的引用
  • at(n):返回vector中第n个元素的引用
  • front():返回vector中第一个元素的引用
  • back():返回vector中最后一个元素的引用
  • data():返回一个指针,指向vector内部用于存储元素的起始内存
#include <bits/stdc++.h>
using namespace std;

int main()
{
	vector<int> g1;

	for (int i = 1; i <= 10; i++)
		g1.push_back(i * 10);

	cout << "\nReference operator [g] : g1[2] = " << g1[2];  //30

	cout << "\nat : g1.at(4) = " << g1.at(4);  //50

	cout << "\nfront() : g1.front() = " << g1.front(); //10

	cout << "\nback() : g1.back() = " << g1.back();  //100

	int* pos = g1.data();
	cout << "\nThe first element is " << *pos;  //10
	return 0;
}

4.修改元素

  • assign():将值分配给vector元素(替换之前的值)
  • push_back():从vector的尾部加入元素
  • pop_back():删除vector尾部的元素
  • insert():在指定位置的元素之前插入新元素
  • erase():用于从容器中删除指定位置或范围内的元素
  • swap():用于将一个vector与另一个相同类型的vector交换内容,大小可能不同
  • clear():用于删除vector的所有元素
  • emplace():通过在某个位置插入新元素来扩展容器
    • insert的不同是,insert需要构造好元素然后再拷贝,而emplace直接在容器中构造,避免额外的复制操作
  • emplace_back():用于向vector的末尾插入新元素
#include <bits/stdc++.h>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	v.assign(5, 10);

	cout << "The vector elements are: ";  //10 10 10 10 10
	for (int i = 0; i < v.size(); i++)
		cout << v[i] << " ";

	v.push_back(15);
	int n = v.size();
	cout << "\nThe last element is: " << v[n - 1];  //15

	v.pop_back();
	cout << "\nThe vector elements are: ";  //10 10 10 10 10
	for (int i = 0; i < v.size(); i++)
		cout << v[i] << " ";

	v.insert(v.begin(), 5);
	cout << "\nThe first element is: " << v[0];  //5

	v.erase(v.begin());
	cout << "\nThe first element is: " << v[0];  //10

	v.emplace(v.begin(), 5);
	cout << "\nThe first element is: " << v[0];  //5

	v.emplace_back(20);
	n = v.size();
	cout << "\nThe last element is: " << v[n - 1];  //20

	v.clear();
	cout << "\nVector size after clear(): " << v.size();  //0


	vector<int> v1, v2;
	v1.push_back(1);v1.push_back(2);
	v2.push_back(3);v2.push_back(4);

	cout << "\n\nVector 1: ";  //1 2
	for (int i = 0; i < v1.size(); i++)
		cout << v1[i] << " ";

	cout << "\nVector 2: ";  //3 4
	for (int i = 0; i < v2.size(); i++)
		cout << v2[i] << " ";

	// Swaps v1 and v2
	v1.swap(v2);
	cout << "\nAfter Swap \nVector 1: ";  //3 4
	for (int i = 0; i < v1.size(); i++)
		cout << v1[i] << " ";

	cout << "\nVector 2: ";  //1 2
	for (int i = 0; i < v2.size(); i++)
		cout << v2[i] << " ";
}

3 deque

deque是双端队列,可以在头或尾进行插入和删除操作。它与vector类似,但在插入和删除元素时更高效,它不保证连续的存储分配。

下面给出一个简单的例子,这些用法在前面的容器中都有用到,故不做详细介绍:

#include <deque>
#include <iostream>

using namespace std;

void showdq(deque<int> g)
{
	deque<int>::iterator it;
	for (it = g.begin(); it != g.end(); ++it)
		cout << '\t' << *it;
	cout << '\n';
}

int main()
{
	deque<int> gquiz;
	gquiz.push_back(10);
	gquiz.push_front(20);
	gquiz.push_back(30);
	gquiz.push_front(15);
	cout << "The deque gquiz is : ";
	showdq(gquiz);

	cout << "\ngquiz.size() : " << gquiz.size();
	cout << "\ngquiz.max_size() : " << gquiz.max_size();

	cout << "\ngquiz.at(2) : " << gquiz.at(2);
	cout << "\ngquiz.front() : " << gquiz.front();
	cout << "\ngquiz.back() : " << gquiz.back();

	cout << "\ngquiz.pop_front() : ";
	gquiz.pop_front();
	showdq(gquiz);

	cout << "\ngquiz.pop_back() : ";
	gquiz.pop_back();
	showdq(gquiz);

	return 0;
}

deque的方法

MethodDefinition
insert()插入元素并返回指向新插入元素中的第一个元素的迭代器
rbegin()返回一个反向迭代器,指向deque的最后一个元素(即反向开始)
rend()返回一个反向迭代器,指向deque的开始之前的位置(被认为是反向结束)
cbegin()返回一个指向容器的第一个元素的常量迭代器,只能用于遍历
max_size()返回deque容器可以容纳的最大元素数量
assign()将值分配给相同或不同的deque容器
resize()更改deque的大小
push_front()/push_back()在deque头/尾增加元素
pop_front()/pop_back()在deque头/尾删除元素
front()/back()获得deque容器的第一个/最后一个元素的引用
clear()/erase()删除deque容器的所有元素/从指定位置或范围内删除容器中的元素
empty()/size()检查deque容器是否为空/返回deque容器的大小或其中元素的数量
[]用于引用操作符内指定位置的元素
at()/swap()引用作为某个索引上的元素/交换具有相同类型和大小的两个deque的内容
begin()/end()返回指向deque容器的第一个/最后一个元素的迭代器
emplace_front()/emplace_back()将新元素插入deque的头/尾

4 forward_list

  • 前向列表(Forward List)在C++11中引入,它实现了单向链表。
  • 前向列表在插入、删除和移动操作(如排序)方面比其他容器更有用,允许在常数时间内插入和删除元素。
  • 前向列表仅跟踪下一个元素的位置,而双向链表(list)跟踪下一个和前一个元素的位置
  • 前向列表的缺点是无法逆向迭代,而且不能直接访问其各个元素。
  • 前向列表在只需要前向遍历时更受青睐,因为可以节省空间。比如:哈希中的链式处理、图的邻接表表示等。

前向列表的操作

1.assign():用于为前向列表分配值

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

int main()
{
	forward_list<int> flist1;
	forward_list<int> flist2;
	forward_list<int> flist3;

	flist1.assign({ 1, 2, 3 });
	flist2.assign(5, 10);
	flist3.assign(flist1.begin(), flist1.end());


	cout << "The elements of first forward list are : ";
	for (int& a : flist1)
		cout << a << " ";  //1 2 3
	cout << endl;

	cout << "The elements of second forward list are : ";
	for (int& b : flist2)
		cout << b << " ";  //10 10 10 10 10
	cout << endl;

	cout << "The elements of third forward list are : ";
	for (int& c : flist3)
		cout << c << " ";  //1 2 3
	cout << endl;

	return 0;
}

2.push_front():将元素插入前向列表的第一个位置。该函数的值被复制到容器中第一个元素之前的空间

3.emplace_front():与push_front类似,但在这个函数中没有复制操作

4.pop_front():删除列表的第一个元素

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

int main()
{
	forward_list<int> flist = { 10, 20, 30, 40, 50 };

	flist.push_front(60);
	cout << "The forward list after push_front operation : ";
	for (int& c : flist)
		cout << c << " ";  //60 10 20 30 40 50
	cout << endl;

	flist.emplace_front(70);
	cout << "The forward list after emplace_front "
			"operation : ";
	for (int& c : flist)
		cout << c << " ";  //70 60 10 20 30 40 50
	cout << endl;

	flist.pop_front();
	cout << "The forward list after pop_front operation : ";
	for (int& c : flist)
		cout << c << " ";  //60 10 20 30 40 50
	cout << endl;

	return 0;
}

5.insert_after():在前向列表的任何位置插入元素

6.emplace_after():与insert_after类似,但元素直接创建,而不进行复制操作

7.erase_after():从前向列表的特定位置删除元素

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

int main()
{
	forward_list<int> flist = { 10, 20, 30 };
	forward_list<int>::iterator ptr;

	ptr = flist.insert_after(flist.begin(), { 1, 2, 3 });

	cout << "The forward list after insert_after operation :"
	for (int& c : flist)
		cout << c << " ";  //10 1 2 3 20 30
	cout << endl;

	ptr = flist.emplace_after(ptr, 2);
	cout << "The forward list after emplace_after operation : ";
	for (int& c : flist)
		cout << c << " ";  //10 1 2 3 2 20 30
	cout << endl;

	ptr = flist.erase_after(ptr);
	cout << "The forward list after erase_after operation : ";
	for (int& c : flist)
		cout << c << " ";  //10 1 2 3 2 30
	cout << endl;

	ptr=flist.erase_after(flist.begin(), flist.end());
	cout << "The forward list after erase_after (range) operation : ";
	for (int& c : flist)
		cout << c << " ";  //10
	cout << endl; 

	return 0;
}

8.remove():删除特定元素,其参数指定了要删除的元素

9.remove_if():根据参数中的条件来进行删除操作

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

int main()
{
	forward_list<int> flist = { 10, 20, 30, 25, 40, 40 };
	flist.remove(40);

	cout << "The forward list after remove operation : ";
	for (int& c : flist)
		cout << c << " ";  //10 20 30 25
	cout << endl;

	flist.remove_if([](int x) { return x > 20; });
	cout << "The forward list after remove_if operation : ";
	for (int& c : flist)
		cout << c << " ";  //10 20
	cout << endl;

	return 0;
}
  • 其中[](int x) { return x > 20; } 是Lambda表达式,本文不做介绍。

10.clear():这个函数从列表中删除所有的元素

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

int main()
{
	forward_list<int> flist{ 1, 2, 3, 4, 5 }; 

	cout<<"Forward list: ";
	for(auto i: flist)
	    cout<<i<<" ";  //1 2 3 4 5
	cout<<endl;

	flist.clear();
	
	cout<<"Forward list after using clear function: ";
	for (auto it = flist.begin(); it != flist.end(); ++it)
		cout << ' ' << *it; //无输出
	cout<<endl;

	return 0;
}

11.splice_after():将元素从一个前向列表传输到另一个前向列表

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

int main()
{
	forward_list<int> flist1 = { 10, 20, 30 };
	forward_list<int> flist2 = { 40, 50, 60 };

	flist2.splice_after(flist2.begin(), flist1);

	cout << "The forward list after splice_after operation : ";
	for (int& c : flist2)
		cout << c << " ";  //40 10 20 30 50 60
	cout << endl;

	return 0;
}

12.其它方法

MethodDefinition
front()返回前向列表第一个元素的引用
begin()/end()返回一个迭代器,指向前向列表容器的第一个/最后一个元素
cbegin()返回一个指向前向列表的第一个元素的常量迭代器
cend()返回一个指向前向列表的尾后元素的常量迭代器。
before_begin()/cbefore_begin()返回一个迭代器/常量迭代器,指向前向列表的第一个元素之前的位置
max_size()返回可以容纳的最大元素数
resize()改变前向列表的大小
unique()移除所有连续的重复元素
reverse()颠倒前向列表中元素的顺序

5 List

列表(Lists)是一种允许非连续内存分配的双向序列容器。与vector相比,列表遍历较慢,但一旦找到位置,插入和删除操作非常快。

直接来看一个例子:

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

int main()
{
	list<int> gqlist{12,45,8,6};
	for (auto i : gqlist) {
		cout << i << ' ';  //12 45 8 6
	}
	return 0;
}

列表的基本操作

  • front():返回列表中的第一个元素的值
  • back():返回列表中的最后一个元素的值
  • push_front():在列表的开头添加一个新元素
  • push_back():在列表的末尾添加一个新元素
  • pop_front():移除列表的第一个元素。
  • pop_back():移除列表的最后一个元素
  • insert():在指定位置之前插入新元素
  • size():返回列表中元素的数量
  • begin():返回一个指向列表的第一个元素的迭代器
  • end():返回一个指向理论上的最后一个元素的迭代器
#include <iostream>
#include <iterator>
#include <list>
using namespace std;

void showlist(list<int> g)
{
	list<int>::iterator it;
	for (it = g.begin(); it != g.end(); ++it)
		cout << ' ' << *it;
	cout << '\n';
}

int main()
{
	list<int> gqlist1, gqlist2;

	for (int i = 0; i < 10; ++i) {
		gqlist1.push_back(i * 2);
		gqlist2.push_front(i * 3);
	}
	cout << "\nList 1 (gqlist1) is : ";
	showlist(gqlist1);  //0 2 4 6 8 10 12 14 16 18

	cout << "\nList 2 (gqlist2) is : ";
	showlist(gqlist2);  //27 24 21 18 15 12 9 6 3 0

	cout << "\ngqlist1.front() : " << gqlist1.front();  //0
	cout << "\ngqlist1.back() : " << gqlist1.back();  //18

	cout << "\ngqlist1.pop_front() : ";
	gqlist1.pop_front();
	showlist(gqlist1);  //2 4 6 8 10 12 14 16 18

	cout << "\ngqlist2.pop_back() : ";
	gqlist2.pop_back();
	showlist(gqlist2);  //27 24 21 18 15 12 9 6 3

	cout << "\ngqlist1.reverse() : ";
	gqlist1.reverse();
	showlist(gqlist1);  //18 16 14 12 10 8 6 4 2

	cout << "\ngqlist2.sort(): ";
	gqlist2.sort();
	showlist(gqlist2);  //3 6 9 12 15 18 21 24 27

	return 0;
}

其它方法

FunctionsDefinition
rbegin()/rend()返回指向列表最后一个元素/开始之前位置的反向迭代器
cbegin()/cend()返回一个指向列表开始/结束的常量随机访问迭代器
crbegin()/crend()返回一个指向列表的最后一个/第一个元素的常量反向迭代器
empty()返回列表是否为空
erase()从列表中移除一个单一元素或一系列元素
assign()通过替换当前元素并调整列表大小来为列表分配新元素
remove()移除列表中与给定元素相等的所有元素
remove_if()用于移除与作为参数给定的条件为true的所有值的元素
reverse()颠倒列表的顺序
list resize()调整列表容器的大小
sort()按升序对列表进行排序
list max_size()返回列表容器可以容纳的最大元素数量
unique()从列表中移除所有连续重复的元素
emplace_front()/emplace_back()在列表容器的开/尾插入新元素
clear()移除列表容器的所有元素
swap()交换相同类型和大小的另一个列表的内容
splice()将元素从一个列表传输到另一个列表
merge()将两个已排序的列表合并为一个列表
emplace()在给定位置插入新元素来扩展列表
  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

tilblackout

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

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

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

打赏作者

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

抵扣说明:

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

余额充值