C++的标准容器及其应用
数组(array)
数组的特征
数组是固定大小的序列容器:它按严格线性序列,保存排序的特定数量的元素。
在内部,数组除了它包含的元素之外,不保留任何数据(甚至不保留其大小,这是一个模板参数,在编译时固定)。就存储大小而言,它与使用语言的括号语法 ([]) 声明的普通数组一样高效。这个类只是添加了一层成员函数和全局函数,这样数组就可以作为标准容器使用了。
与其他标准容器不同,数组具有固定大小,并且不能通过分配器管理其元素的分配:它们是封装固定大小元素数组的聚合类型。因此,它们不能动态扩展或收缩。
零大小的数组是有效的,但不能调用它的成员函数(比如front, back和data)。
与标准库中的其他容器不同,交换两个数组容器是一种线性操作,涉及单独交换范围中的所有元素,这通常是效率相当低的操作。另一方面,这允许两个容器中的元素的迭代器保持其原始容器关联。
数组容器的另一个独特功能是,它们可以被视为元组(tuple)对象: array 能重载 get 函数以访问数组的元素,就好像它是元组一样,拥有专门的 tuple_size 和 tuple_element 类型。
应用实列
成员函数,size()。
#include <iostream>
#include <array>
using namespace std;
int main ()
{
array<int,5> arr_int = {0};
cout << "size of arr_int: " << arr_int.size() << endl;
cout << "sizeof(arr_int): " << sizeof(arr_int) << endl;
return 0;
}
size of arr_int: 5
sizeof(arr_int): 20
成员函数,data()。
#include <iostream>
#include <cstring>
#include <array>
using namespace std;
int main ()
{
const char* cstr = "Test string";
array<char,12> charr;
memcpy (charr.data(),cstr,12);
cout << charr.data() << '\n';
return 0;
}
// output
Test string
前向列表(forward_list)
前向列表的特征
前向列表是序列容器,可以用不变的时间,在序列中任何位置进行插入和删除。
前向列表作为单链表实现;单链表可以将它们包含的每个元素存储在不同,且不相关的存储位置。通过与序列中下一个元素的链接的每个元素的关联来保持顺序。
forward_list 容器和列表容器之间的主要设计区别在于,第一个容器仅在内部保留到下一个元素的链接,而后者为每个元素保留两个链接:一个指向下一个元素,一个指向前一个元素,从而实现高效双向迭代,但每个元素消耗额外的存储空间,并且插入和删除元素的时间开销稍高。因此,forward_list 对象比 list 对象更有效,尽管它们只能向前迭代。
与其他基本标准序列容器(数组、向量和双向队列)相比,在容器内任何位置插入、提取和移动元素,forward_list 通常表现更好,因此在密集使用这些元素的算法(如排序算法)中也表现得更好。
与其他序列容器相比,forward_lists 和list的主要缺点是它们无法通过位置直接访问元素;例如,要访问forward_list中的第六个元素,必须从开头迭代到该位置,这需要这些元素之间的距离呈线性时间。它们还消耗一些额外的内存来保存与每个元素关联的链接信息(这对于小型元素的大型列表可能是一个重要因素)。
forward_list 类模板在设计时就考虑到了效率:根据设计,它与简单的手写 C 风格单链表一样高效,事实上,它是唯一出于效率考虑,而故意缺少 size 成员函数的标准容器:由于其作为链表的性质,具有需要恒定时间的大小成员将需要它为其大小保留一个内部计数器(如列表那样)。这会消耗一些额外的存储空间,并使插入和删除操作的效率稍微降低。如果需要获取forward_list对象的大小,可以使用距离算法,加上开始和结束参数,这需要线性操作时间。
应用实列
插入元素
#include <iostream>
#include <forward_list>
using namespace std;
int main ()
{
forward_list<int> mylist;
forward_list<int>::iterator it;
it = mylist.insert_after ( mylist.before_begin(), 10 );
it = mylist.insert_after ( it, 2, 20 );
it = mylist.begin();
it = mylist.insert_after ( it, {1,5,3} );
cout << "mylist contains:";
for (auto x: mylist) cout << ' ' << x;
cout << endl;
return 0;
}
// output
mylist contains: 10 1 5 3 20 20
列表(list)
列表的特征
list是序列容器,在list中的任何位置,进行插入和擦除操作,只需要恒定时间。list可以双向迭代。
list容器是用双向链表实现的;双向链表可以将它们包含的每个元素存储在不同且不相关的存储位置。通过与每个元素的关联在内部保持顺序。
它们与forward_list非常相似:主要区别在于forward_list对象是单向链表,因此它们只能向前迭代,以换取更小和更高效的代价。
与其他基本标准序列容器(数组、向量和双端队列)相比,list在已获得迭代器的容器内的任何位置插入、提取和移动元素方面通常表现更好,因此经常出现在密集使用元素操作的算法中,例如排序算法。
与其他序列容器相比,list和前向列表的主要缺点是,它们无法通过位置直接访问元素;例如,要访问列表中的第六个元素,必须从已知位置(