C++ list容器详解
在 C++ 中,虽然 vector 容器解决了数组存在的一些问题,但本质来讲 vector 还是一种数组,其元素在内存上是连续排列的,插入和删除元素的效率比较低。为了弥补 vector 的不足,C++ 引入了 list 容器。
list 是一个双向链表容器,定义在
list 容器最大的优点是插入、删除元素的效率高。因为链表的节点间不是紧邻排列的,而是通过指针关联起来的,这样在插入、删除元素时只要重新设定指针的指向即可,而不需要移动元素。
list 容器的缺点是查找元素比较慢,因为元素间不是紧邻排列的,所以不能使用下标访问某个节点,即不能随机访问元素。
下表罗列出了 list 模板类提供的所有成员函数以及各自的功能。
有关表示各个成员函数的语法格式,读者不需要死记硬背,需要时直接去查 C++ 标准库即可,这里不再过多赘述。
下面的 C++ 代码演示了表中部分成员函数的用法:
list 是一个双向链表容器,定义在
<list>
头文件中。list 容器中的元素不是连续排放的,元素与元素之间是靠指针维系,即每一个元素都配备两个指针,分别指向它的下一个元素和前一个元素。list 容器最大的优点是插入、删除元素的效率高。因为链表的节点间不是紧邻排列的,而是通过指针关联起来的,这样在插入、删除元素时只要重新设定指针的指向即可,而不需要移动元素。
list 容器的缺点是查找元素比较慢,因为元素间不是紧邻排列的,所以不能使用下标访问某个节点,即不能随机访问元素。
list容器的构造
list 容器本质是一个模板类,它提供了多种构造函数,以便适应不同的应用场景,包括:// 创建一个空的list容器 explicit list (const allocator_type& alloc = allocator_type()); // 创建一个有 n 个元素的 list 容器,这些元素进行默认初始化 explicit list (size_type n); // 创建一个有 n 个元素的 list 容器,并将这些元素初始化为 val list (size_type n, const value_type& val, const allocator_type& alloc = allocator_type()); // 从两个迭代器 first 和 last 指定的范围中复制元素到新的 list 容器中 template <class InputIterator> list (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()); // 拷贝构造函数,创建一个与 x 相同的新 list 容器 list (const list& x); list (const list& x, const allocator_type& alloc); // 移动构造函数,创建一个新的 list 容器,并从 x 中“移动”数据 list (list&& x); list (list&& x, const allocator_type& alloc); // 从一个初始化列表 il 中复制元素到新的 list 容器中 list (initializer_list<value_type> il, const allocator_type& alloc = allocator_type());下面是一个完整的 C++ 示例,演示了用各种构造函数来创建 list 容器。
#include <iostream> #include <list> int main() { // 使用默认构造函数创建一个空的 list std::list<int> list1; std::cout << "list1: "; for (const auto& elem : list1) { std::cout << elem << ' '; } std::cout << '\n'; // 创建一个含有 5 个元素的 list,每个元素进行默认初始化(对于 int 类型,这通常意味着初始化为 0) std::list<int> list2(5); std::cout << "list2: "; for (const auto& elem : list2) { std::cout << elem << ' '; } std::cout << '\n'; // 创建一个含有 3 个元素的 list,每个元素都初始化为 7 std::list<int> list3(3, 7); std::cout << "list3: "; for (const auto& elem : list3) { std::cout << elem << ' '; } std::cout << '\n'; // 使用范围构造函数,从另一个 list(list3)复制元素 std::list<int> list4(list3.begin(), list3.end()); std::cout << "list4: "; for (const auto& elem : list4) { std::cout << elem << ' '; } std::cout << '\n'; // 使用拷贝构造函数,从另一个 list(list4)复制元素 std::list<int> list5(list4); std::cout << "list5: "; for (const auto& elem : list5) { std::cout << elem << ' '; } std::cout << '\n'; // 使用移动构造函数,从另一个 list(list5)移动元素 std::list<int> list6(std::move(list5)); std::cout << "list6: "; for (const auto& elem : list6) { std::cout << elem << ' '; } std::cout << '\n'; // 使用 initializer_list 初始化 std::list<int> list7 = {1, 2, 3, 4, 5}; std::cout << "list7: "; for (const auto& elem : list7) { std::cout << elem << ' '; } std::cout << '\n'; return 0; }运行结果为:
list1:
list2: 0 0 0 0 0
list3: 7 7 7
list4: 7 7 7
list5: 7 7 7
list6: 7 7 7
list7: 1 2 3 4 5
list容器的使用
list 容器常做的操作就是插入和删除元素,可以借助 list 模板类中提供的成员函数实现。下表罗列出了 list 模板类提供的所有成员函数以及各自的功能。
成员函数 | 功能 |
---|---|
begin() | 返回指向容器中第一个元素的双向迭代器。 |
end() | 返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器。 |
rbegin() | 返回指向最后一个元素的反向双向迭代器。 |
rend() | 返回指向第一个元素所在位置前一个位置的反向双向迭代器。 |
cbegin() | 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
cend() | 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crbegin() | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
empty() | 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。 |
size() | 返回当前容器实际包含的元素个数。 |
max_size() | 返回容器所能包含元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。 |
front() | 返回第一个元素的引用。 |
back() | 返回最后一个元素的引用。 |
assign() | 用新元素替换容器中原有内容。 |
emplace_front() | 在容器头部生成一个元素。该函数和 push_front() 的功能相同,但效率更高。 |
push_front() | 在容器头部插入一个元素。 |
pop_front() | 删除容器头部的一个元素。 |
emplace_back() | 在容器尾部直接生成一个元素。该函数和 push_back() 的功能相同,但效率更高。 |
push_back() | 在容器尾部插入一个元素。 |
pop_back() | 删除容器尾部的一个元素。 |
emplace() | 在容器中的指定位置插入元素。该函数和 insert() 功能相同,但效率更高。 |
insert() | 在容器中的指定位置插入元素。 |
erase() | 删除容器中一个或某区域内的元素。 |
swap() | 交换两个容器中的元素,必须保证这两个容器中存储的元素类型是相同的。 |
resize() | 调整容器的大小。 |
clear() | 删除容器存储的所有元素。 |
splice() | 将一个 list 容器中的元素插入到另一个容器的指定位置。 |
remove(val) | 删除容器中所有等于 val 的元素。 |
remove_if() | 删除容器中满足条件的元素。 |
unique() | 删除容器中相邻的重复元素,只保留一个。 |
merge() | 合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。 |
sort() | 通过更改容器中元素的位置,将它们进行排序。 |
reverse() | 反转容器中元素的顺序。 |
有关表示各个成员函数的语法格式,读者不需要死记硬背,需要时直接去查 C++ 标准库即可,这里不再过多赘述。
下面的 C++ 代码演示了表中部分成员函数的用法:
#include <iostream> #include <list> int main() { // 构造一个 list 容器 std::list<int> myList = {1, 2, 3, 4, 5}; // 在列表的开头和末尾添加元素 myList.push_front(0); myList.push_back(6); // 显示列表元素 std::cout << "List elements: "; for(auto it = myList.begin(); it != myList.end(); ++it) { std::cout << *it << ' '; } std::cout << std::endl; // 删除第一个和最后一个元素 myList.pop_front(); myList.pop_back(); // 再次显示列表元素 std::cout << "List elements after pop: "; for(const auto& elem : myList) { std::cout << elem << ' '; } std::cout << std::endl; return 0; }运行结果为:
List elements: 0 1 2 3 4 5 6
List elements after pop: 1 2 3 4 5