容器是一个持有其他对象元素集合的容器对象,以类模板的形式实现,可以灵活地支持不同类型的元素。容器负责管理其元素的存储空间,并提供成员函数来直接或通过迭代器(类似于指针的引用对象)访问它们。
C++中有多种种类的容器,包括:顺序容器(Sequence Containers
)、关联容器(Associative Containers
)、无序容器(Unordered Containers
)和容器适配器(Container Adapters
)。
这篇文章先来详细介绍一下顺序容器的用法。顺序容器实现了可以按顺序访问的数据结构,它包括array
(静态连续数组)、vector
(动态连续数组)、deque
(双端队列)、forward_list
(单向链表)和list
(双向链表)。
C++11中的array
类的引入为C风格数组提供了更好的替代方案。array
类相对于C数组的优势在于:
array
类知道自己的大小,因此在传递给函数时,不需要将数组的大小作为单独的参数传递array
类不会退化为指针array
类的操作
1.访问数组元素:at()
/get()
/[]
#include
#include // for array, at()
#include // for get()
using namespace std;
int main()
{
array 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 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 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 ar = {1, 2, 3, 4, 5, 6};
array ar1 = {7, 8, 9, 10, 11, 12};
ar.swap(ar1); //此时ar和ar1的数组调换
5.empty():如果array
元素个数为0返回真
6.fill():用某个值填充整个array
array ar;
array ar1;
ar1.empty()? cout << "Array empty" << endl:cout << "Array not empty" << endl;//empty
ar.fill(0);//ar的六个元素均为0
vector
在插入或删除元素时,它们能够自动调整大小。元素被放置在连续的存储空间中,以便可以使用迭代器访问和遍历它们。插入元素的所需的时间不同,因为需要找一个更大的内存以扩展数组大小。而删除元素的时间则是确定的,因为无需寻找新的内存。
1.迭代器(iterators
)
begin()
:返回指向vector
中第一个元素的迭代器end()
:返回指向vector
中最后一个元素之后的理论元素的迭代器rbegin()
:返回指向vector
中最后一个元素(反向开始)的反向迭代器,它从最后一个元素往第一个元素移动rend()
:返回指向vector
中第一个元素之前的理论元素(视为反向结束)的反向迭代器cbegin()
:返回指向vector
中第一个元素的常量迭代器cend()
:返回指向vector
中最后一个元素之后的理论元素的常量迭代器crbegin()
:返回指向vector
中最后一个元素(反向开始)的常量反向迭代器,它从最后一个元素往第一个元素移动crend()
:返回指向vector
中第一个元素之前的理论元素(视为反向结束)的常量反向迭代器。注意:这里的常量迭代器指的是不允许使用迭代器修改元素的值。下面来看一下例子:
#include
#include
using namespace std;
int main()
{
vector 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
#include
using namespace std;
int main()
{
vector 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
using namespace std;
int main()
{
vector 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
#include
using namespace std;
int main()
{
vector 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 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] << " ";
}
deque
是双端队列,可以在头或尾进行插入和删除操作。它与vector
类似,但在插入和删除元素时更高效,它不保证连续的存储分配。
下面给出一个简单的例子,这些用法在前面的容器中都有用到,故不做详细介绍:
#include
#include
using namespace std;
void showdq(deque g)
{
deque::iterator it;
for (it = g.begin(); it != g.end(); ++it)
cout << '\t' << *it;
cout << '\n';
}
int main()
{
deque 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
的方法:
Method | Definition |
---|---|
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的头/尾 |
Forward List
)在C++11中引入,它实现了单向链表。list
)跟踪下一个和前一个元素的位置前向列表的操作:
1.assign():用于为前向列表分配值
#include
#include
using namespace std;
int main()
{
forward_list flist1;
forward_list flist2;
forward_list 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
#include
using namespace std;
int main()
{
forward_list 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
#include
using namespace std;
int main()
{
forward_list flist = { 10, 20, 30 };
forward_list::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
#include
using namespace std;
int main()
{
forward_list 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
#include
using namespace std;
int main()
{
forward_list flist{ 1, 2, 3, 4, 5 };
cout<<"Forward list: ";
for(auto i: flist)
cout<
11.splice_after():将元素从一个前向列表传输到另一个前向列表
#include
#include
using namespace std;
int main()
{
forward_list flist1 = { 10, 20, 30 };
forward_list 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.其它方法
Method | Definition |
---|---|
front() | 返回前向列表第一个元素的引用 |
begin()/end() | 返回一个迭代器,指向前向列表容器的第一个/最后一个元素 |
cbegin() | 返回一个指向前向列表的第一个元素的常量迭代器 |
cend() | 返回一个指向前向列表的尾后元素的常量迭代器。 |
before_begin()/cbefore_begin() | 返回一个迭代器/常量迭代器,指向前向列表的第一个元素之前的位置 |
max_size() | 返回可以容纳的最大元素数 |
resize() | 改变前向列表的大小 |
unique() | 移除所有连续的重复元素 |
reverse() | 颠倒前向列表中元素的顺序 |
列表(Lists
)是一种允许非连续内存分配的双向序列容器。与vector
相比,列表遍历较慢,但一旦找到位置,插入和删除操作非常快。
直接来看一个例子:
#include
#include
using namespace std;
int main()
{
list 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
#include
#include
using namespace std;
void showlist(list g)
{
list::iterator it;
for (it = g.begin(); it != g.end(); ++it)
cout << ' ' << *it;
cout << '\n';
}
int main()
{
list 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;
}
其它方法:
Functions | Definition |
---|---|
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() | 在给定位置插入新元素来扩展列表 |