什么是list
在C++标准库中,std::list
是一个双向链表容器,用于存储一系列元素。与 std::vector
和 std::deque
等容器不同,std::list
使用链表的数据结构来组织元素,因此在某些操作上具有独特的优势和性能特点。以下是关于 std::list 的详细介绍:
特点和优势
双向链表结构:
std::list
内部使用双向链表来存储元素。这意味着在插入和删除元素时,不会引起其他元素的内存重新分配和复制,因此在这些操作上具有常数时间复杂度。插入和删除操作效率高: 由于链表结构,插入和删除元素的操作效率非常高。对于大量的插入和删除操作,std::list 往往比其他容器更具优势。
迭代器的稳定性: 在
std::list
中,插入和删除元素不会使迭代器失效,除非删除的正是迭代器所指向的元素。这使得在遍历过程中进行插入和删除操作更加方便和安全。空间占用:
std::list
每个元素都需要存储一个指向前后元素的指针,因此相对于数组型的容器,std::list
的空间占用会更高。
基本操作
push_back()
和push_front()
: 在链表的末尾和开头插入元素。
pop_back()
和pop_front()
: 删除链表的末尾和开头的元素。
insert()
: 在指定位置插入元素。
erase()
: 删除指定位置的元素。
begin()
和end()
: 返回指向链表起始元素和尾部元素后一个位置的迭代器。
size()
: 返回链表中的元素数量。
empty()
: 检查链表是否为空。
clear()
: 清空链表中的所有元素。
示例用法
#include <iostream> #include <list> int main() { std::list<int> myList; myList.push_back(1); myList.push_back(2); myList.push_back(3); myList.pop_front(); myList.insert(std::next(myList.begin()), 4); for (const auto& num : myList) { std::cout << num << " "; } std::cout << std::endl; return 0; }
在使用 std::list
时,需要权衡其优势和劣势,根据实际场景来选择合适的容器。当需要频繁插入和删除元素,且不需要随机访问时,std::list
可能是一个很好的选择。但需要注意的是,由于链表的特性,std::list
并不适用于需要快速随机访问元素的场景,因为访问某个位置的元素需要遍历链表。
与其他序列式容器(如 std::vector 和 std::deque)相比,std::list 显著的区别和优势
优势:
插入和删除效率高: 由于
std::list
是双向链表,插入和删除操作在常数时间内完成,不需要涉及内存的重新分配和元素的复制。这使得std::list
在大量插入和删除操作时非常高效。迭代器的稳定性:
std::list
的插入和删除操作不会使迭代器失效,除非删除的正是迭代器所指向的元素。这使得在遍历过程中进行插入和删除操作更加方便和安全。空间占用相对稳定:
std::list
的空间占用相对稳定,插入和删除操作不会影响其他元素的空间占用。
劣势:
不支持随机访问: 由于链表的结构,
std::list
不支持像数组一样的随机访问。访问某个位置的元素需要从链表的开头或结尾开始遍历。额外的指针开销:
std::list
中的每个元素都需要存储指向前后元素的指针,这使得空间占用相对其他容器更高。缓存效率低: 由于链表中元素在内存中的存储位置不连续,导致在访问链表元素时,缓存命中率较低,可能影响性能。
迭代器的使用限制:
std::list
的迭代器不支持与普通指针类似的算术操作(如 + 和 -),因此无法像std::vector
那样灵活地进行迭代器操作。
成员类型
list构造函数
当使用 std::list
类创建对象时,可以使用不同的构造函数来满足不同的初始化需求。下面详细介绍每个构造函数及其使用示例:
1. default (1)
这个构造函数用于创建一个空的 std::list
容器。它可以接受一个可选的分配器参数,用于指定内存分配策略。
std::list<int> myList; // 创建一个空的 std::list 容器
2. fill (2)
这个构造函数用于创建一个包含 n 个元素的 std::list
容器,并将这些元素初始化为 val
。你可以通过传递不同的 val
值来创建一个包含相同值的容器。同样,也可以传递一个可选的分配器参数。
std::list<int> myList(5, 42); // 创建一个包含 5 个元素,每个元素都是 42 的 std::list 容器
3.range (3)
这个构造函数使用迭代器范围 [first, last)
中的元素创建一个 std::list
容器。这使你可以通过一个迭代器范围来初始化容器。同样,它也接受一个可选的分配器参数。
std::vector<int> vec = {1, 2, 3, 4, 5}; std::list<int> myList(vec.begin(), vec.end()); // 从迭代器范围内的元素创建 std::list 容器
4. copy (4)
这个构造函数用于创建一个与已存在的 std::list
容器 x
相同的副本。它会将 x
中的所有元素拷贝到新的容器中。这是一个拷贝构造函数。
std::list<int> originalList = {1, 2, 3, 4, 5}; std::list<int> copiedList(originalList); // 创建一个原容器的副本
这些构造函数提供了不同的初始化方式,以便根据具体需求来创建 std::list 容器。根据你的数据源和其他条件,选择适合的构造函数来创建容器对象。
list迭代器(Iterators)
1. begin()
iterator begin() noexcept;
这个版本的 begin()
返回一个迭代器,可以用于修改容器内的元素。noexcept
表示这个函数不会抛出异常。
std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::iterator it = myList.begin(); // 获取可修改元素的迭代器 *it = 10; // 修改第一个元素的值为 10
const_iterator begin() const noexcept;
这个版本的 begin()
返回一个只读的迭代器,用于在不修改容器的情况下访问元素。const
表示这个函数不会修改容器。
const std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::const_iterator cit = myList.begin(); // 获取只读元素的迭代器 int firstElement = *cit; // 读取第一个元素的值
2. end()
iterator end() noexcept;
这个版本的 end()
返回一个迭代器,可以用于修改容器内的元素。noexcept
表示这个函数不会抛出异常。这个迭代器指向的位置实际上是容器的末尾位置之后一个虚拟的位置,所以它并不指向容器内的任何元素。
std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::iterator it = myList.end(); // 获取可修改元素的迭代器 --it; // 将迭代器前移一个位置,指向最后一个元素 *it = 20; // 修改最后一个元素的值为 20
const_iterator end() const noexcept;
这个版本的 end()
返回一个只读的迭代器,用于在不修改容器的情况下访问元素。const
表示这个函数不会修改容器。同样,这个迭代器也指向虚拟的位置,容器内的最后一个元素之后。
const std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::const_iterator cit = myList.end(); // 获取只读元素的迭代器 --cit; // 将迭代器前移一个位置,指向最后一个元素 int lastElement = *cit; // 读取最后一个元素的值
3. rbegin()
reverse_iterator rbegin() noexcept;
这个版本的 rbegin()
返回一个反向迭代器,可以用于修改容器内的元素。noexcept
表示这个函数不会抛出异常。这个反向迭代器指向容器内的最后一个元素,可以通过递减操作符 --
往前遍历容器。
std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::reverse_iterator rit = myList.rbegin(); // 获取可修改元素的反向迭代器 *rit = 10; // 修改最后一个元素的值为 10 ++rit; // 将反向迭代器往前移动一个位置,指向倒数第二个元素
const_reverse_iterator rbegin() const noexcept;
这个版本的 rbegin()
返回一个只读的反向迭代器,用于在不修改容器的情况下访问元素。const
表示这个函数不会修改容器。这个反向迭代器同样指向最后一个元素,可以通过递减操作符 --
往前遍历容器。
const std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::const_reverse_iterator crit = myList.rbegin(); // 获取只读元素的反向迭代器 int lastElement = *crit; // 读取最后一个元素的值 ++crit; // 将反向迭代器往前移动一个位置,指向倒数第二个元素
4. rend()
reverse_iterator rend() nothrow;
这个版本的 rend()
返回一个反向迭代器,可以用于修改容器内的元素。nothrow
表示这个函数不会抛出异常。这个反向迭代器指向容器内的位置,位于第一个元素之前,可以通过递减操作符 --
往前遍历容器。
std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::reverse_iterator rit = myList.rend(); // 获取可修改元素的反向迭代器 --rit; // 将反向迭代器往前移动一个位置,指向最后一个元素 *rit = 10; // 修改最后一个元素的值为 10
const_reverse_iterator rend() const nothrow;
这个版本的 rend()
返回一个只读的反向迭代器,用于在不修改容器的情况下访问元素。const
表示这个函数不会修改容器。这个反向迭代器同样指向容器的位置,位于第一个元素之前,可以通过递减操作符 – 往前遍历容器。
const std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::const_reverse_iterator crit = myList.rend(); // 获取只读元素的反向迭代器 --crit; // 将反向迭代器往前移动一个位置,指向最后一个元素 int lastElement = *crit; // 读取最后一个元素的值
5. cbegin()、cend()、crbegin()、crend()
const_iterator cbegin() const noexcept;
这个成员函数返回一个指向容器元素的常量迭代器,指向容器的起始位置。通过常量迭代器,你可以遍历容器的元素,但不能修改它们。
const_iterator cend() const noexcept;
这个成员函数返回一个指向容器元素的常量迭代器,指向容器的结束位置。这个迭代器表示一个超过容器尾部的位置,通常用于迭代器循环的终止条件。
std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::const_iterator cit = myList.cbegin(); // 获取常量迭代器 for (; cit != myList.cend(); ++cit) { std::cout << *cit << " "; // 输出容器中的元素,不修改它们 }
const_reverse_iterator crbegin() const noexcept;
这个成员函数返回一个指向容器元素的常量反向迭代器,指向容器的反向起始位置。通过常量反向迭代器,你可以反向遍历容器的元素,但不能修改它们。
const_reverse_iterator crend() const noexcept;
这个成员函数返回一个指向容器元素的常量反向迭代器,指向容器的反向结束位置。这个迭代器表示一个超过容器首部的位置,通常用于反向迭代器循环的终止条件。
std::list<int> myList = {1, 2, 3, 4, 5}; std::list<int>::const_reverse_iterator crit = myList.crbegin(); // 获取常量反向迭代器 for (; crit != myList.crend(); ++crit) { std::cout << *crit << " "; // 反向输出容器中的元素,不修改它们 }
list容量函数(Capacity)和元素访问函数(Element access)
1. empty()
empty()
是 std::list
容器的一个成员函数,用于判断容器是否为空。它返回一个布尔值,表示容器是否不包含任何元素。函数声明如下:
bool empty() const noexcept;
返回值:如果容器为空,则返回 true,否则返回 false。
使用示例:
#include <iostream> #include <list> int main() { std::list<int> myList; if (myList.empty()) { std::cout << "The list is empty." << std::endl; } else { std::cout << "The list is not empty." << std::endl; } myList.push_back(42); if (myList.empty()) { std::cout << "The list is empty." << std::endl; } else { std::cout << "The list is not empty." << std::endl; } return 0; }
在上面的示例中,首先创建一个空的 std::list
容器 myList
,然后使用 empty()
函数检查容器是否为空,并输出相应的信息。然后,通过 push_back
向容器中添加一个元素,并再次使用 empty()
函数检查容器是否为空,输出相应的信息。
2. size()
size()
是 std::list
容器的一个成员函数,用于返回容器中元素的数量。它返回一个无符号整数类型,表示容器中的元素数量。函数声明如下:
size_type size() const noexcept;
返回值:返回容器中元素的数量,即大小。
使用示例:
#include <iostream> #include <list> int main() { std::list<int> myList; myList.push_back(1); myList.push_back(2); myList.push_back(3); std::cout << "Size of the list: " << myList.size() << std::endl; return 0; }
在上面的示例中,我们首先创建一个 std::list
容器 myList
,然后使用 push_back
函数向容器中添加三个元素。然后使用 size()
函数获取容器的大小,并输出到标准输出。