list(理解和使用)
- 什么是list
- 特点和优势
- 基本操作
- 示例用法
- 与其他序列式容器(如 std::vector 和 std::deque)相比,std::list 显著的区别和优势
- 成员类型
- list构造函数
- 1. default (1)
- 2. fill (2)
- 3.range (3)
- 4. copy (4)
- list迭代器(Iterators)
- 1. begin()
- 2. end()
- 3. rbegin()
- 4. rend()
- 5. cbegin()、cend()、crbegin()、crend()
- list容量函数(Capacity)和元素访问函数(Element access)
- 1. empty()
- 2. size()
- 3. max_size()
- 4. front()
- 5. back()
- list增删查改函数(Modifiers)
- 1. assign
- 2. emplace_front
- 3. push_front
- 4. pop_front
- 5. emplace_back
- 6. push_back
- 7. pop_back
- 8. emplace
- 9. insert
- 10. erase
- 11. swap
- 12. resize
- 13. clear
- list操作函数(Operations)
- 1. splice
- 2. remove
- 3. remove_if
- 4. unique
- 5. merge
- 6. sort
- 7. reverse
- list的迭代器失效
- 结语
什么是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()
函数获取容器的大小,并输出到标准输出。
3. max_size()
max_size()
是 std::list
容器的一个成员函数,用于返回容器可能容纳的最大元素数量,通常受到系统内存限制的影响。它返回一个无符号整数类型,表示容器的最大大小。函数签名如下:
size_type max_size() const noexcept;
返回值:返回容器可能容纳的最大元素数量。
使用示例:
#include <iostream>
#include <list>
int main() {
std::list<int> myList;
std::cout << "Max size of the list: " << myList.max_size() << std::endl;
return 0;
}
在上面的示例中,我们创建了一个空的 std::list
容器 myList
,然后使用 max_size()
函数获取容器的最大大小,并输出到标准输出。请注意,实际可用的最大大小取决于系统内存和其他资源的限制。
4. front()
front()
是 std::list
容器的成员函数,用于返回容器中第一个元素的引用。这个函数有两个版本,一个用于可修改容器的对象,另一个用于只读(const)容器的对象。函数的签名如下:
reference front();
const_reference front() const;
reference:返回一个对容器中第一个元素的非常引用。
const_reference:只有在 const
容器对象上调用时,才返回一个对容器中第一个元素的常引用。
使用示例:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {10, 20, 30};
int& firstElement = myList.front();
const int& constFirstElement = myList.front();
std::cout << "First element: " << firstElement << std::endl;
std::cout << "Const first element: " << constFirstElement << std::endl;
return 0;
}
在上面的示例中,我们创建了一个 std::list
容器 myList
,其中包含三个整数元素。我们使用 front()
函数来获取容器中的第一个元素的引用,分别存储为可修改的引用 firstElement
和只读的常引用 constFirstElement
,然后将它们输出到标准输出。
5. back()
back()
是 std::list 容器的成员函数,用于返回容器中最后一个元素的引用。这个函数有两个版本,一个用于可修改容器的对象,另一个用于只读(const)容器的对象。函数的签名如下:
reference back();
const_reference back() const;
reference:返回一个对容器中最后一个元素的非常引用。
const_reference:只有在 const 容器对象上调用时,才返回一个对容器中最后一个元素的常引用。
使用示例:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {10, 20, 30};
int& lastElement = myList.back();
const int& constLastElement = myList.back();
std::cout << "Last element: " << lastElement << std::endl;
std::cout << "Const last element: " << constLastElement << std::endl;
return 0;
}
在上面的示例中,我们创建了一个 std::list
容器 myList
,其中包含三个整数元素。我们使用 back()
函数来获取容器中的最后一个元素的引用,分别存储为可修改的引用 lastElement
和只读的常引用 constLastElement
,然后将它们输出到标准输出。
list增删查改函数(Modifiers)
1. assign
assign
是 std::list
容器的成员函数,用于将容器的内容替换为新的元素。这个函数有三个不同版本:
使用迭代器范围:
template <class InputIterator>
void assign (InputIterator first, InputIterator last);
这个版本接受两个迭代器参数 first
和 last
,用来指定一个范围。它会将容器的内容替换为范围 [first, last)
内的元素。
使用重复元素:
void assign (size_type n, const value_type& val);
这个版本接受一个整数参数 n
,和一个值 val
。它会将容器的内容替换为 n
个值为 val
的元素。
使用初始化列表:
void assign (initializer_list<value_type> il);
这个版本接受一个初始化列表作为参数。它会将容器的内容替换为初始化列表中的元素。
使用示例:
#include <iostream>
#include <list>
int main() {
std::list<int> myList;
myList.assign({1, 2, 3, 4, 5}); // 使用初始化列表
std::cout << "Size after assigning with initializer list: " << myList.size() << std::endl;
myList.assign(3, 100); // 使用重复元素
std::cout << "Size after assigning with repeated elements: " << myList.size() << std::endl;
std::list<int> anotherList = {10, 20, 30, 40};
myList.assign(anotherList.begin(), anotherList.end()); // 使用迭代器范围
std::cout << "Size after assigning with iterator range: " << myList.size() << std::endl;
return 0;
}
在上面的示例中,我们首先创建了一个空的 std::list
容器 myList
。然后使用不同版本的 assign
函数来分别替换容器的内容。最后,我们输出容器的大小以验证操作是否成功。
2. emplace_front
template <class... Args>
void emplace_front (Args&&... args);
emplace_front
是 std::list
容器的成员函数,用于在容器的开头插入一个新元素。它通过在指定位置直接构造元素,避免了额外的拷贝或移动操作。
这个函数接受可变数量的参数 Args...
,这些参数会被用来构造新元素。使用 emplace_front
可以直接在容器的开头插入元素,而不需要先创建一个临时对象然后再进行插入操作。
使用示例:
#include <iostream>
#include <list>
struct Person {
std::string name;
int age;
Person(const std::string& n, int a) : name(n), age(a) {
std::cout << "Constructing " << name << std::endl;
}
};
int main() {
std::list<Person> personList;
personList.emplace_front("zhangsan", 25);
personList.emplace_front("lisi", 30);
personList.emplace_front("wangwu", 28);
std::cout << "Person list contents:" << std::endl;
for (const auto& person : personList) {
std::cout << "Name: " << person.name << ", Age: " << person.age << std::endl;
}
return 0;
}
在上述示例中,我们首先定义了一个名为 Person
的结构体,它有两个成员变量:name
和 age
。然后我们创建了一个空的 std::list
容器 personList
,使用 emplace_front
函数分别在容器的开头插入了几个新的 Person
对象,直接在插入位置进行构造。
注意,emplace_front
的参数被传递给 Person
类型的构造函数,用于构造新的 Person
对象。这样做避免了额外的拷贝或移动操作,提高了效率。
3. push_front
push_front
是 std::list
容器的成员函数,用于在容器的开头插入一个新元素。
这个函数有两个版本:
void push_front (const value_type& val);
:接受一个常量引用参数,会创建一个新元素并将参数的值拷贝到新元素中。
void push_front (value_type&& val);
:接受一个右值引用参数,用于移动构造一个新元素。这样可以避免额外的拷贝操作,提高了效率。
使用示例:
#include <iostream>
#include <list>
int main() {
std::list<int> myList;
int value = 20;
myList.push_front(value); // Copy insert
std::cout << "List contents:" << std::endl;
for (const auto& num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
myList.push_front(25); // Move insert
std::cout << "List contents after move insert:" << std::endl;
for (const auto& num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在上述示例中,我们首先创建一个空的 std::list
容器 myList
,然后使用 push_front
函数分别进行了常量引用拷贝插入和右值引用移动插入操作。可以看到,右值引用版本的 push_front
更加高效,因为它避免了额外的拷贝操作。
4. pop_front
void pop_front();
是用于从 std::list 的开头移除一个元素的成员函数。它会删除列表中的第一个元素,并且将列表的大小减小一个单位。
示例用法:
std::list<int> myList = {1, 2, 3, 4, 5};
myList.pop_front(); // 移除第一个元素
在上面的例子中,pop_front()
将会移除 1 这个元素,使得列表变为 {2, 3, 4, 5}。
5. emplace_back
template <class... Args> void emplace_back (Args&&... args);
是一个用于在 std::list
的末尾插入新元素的函数。它允许你通过传递构造元素所需的参数来直接在列表的末尾构造元素,避免了额外的拷贝或移动操作。
示例用法:
std::list<std::string> myList;
myList.emplace_back("Hello");
myList.emplace_back("World");
在上面的例子中,emplace_back
函数直接在列表的末尾构造了两个字符串元素,分别是 "Hello"
和 "World"
。
这个函数对于避免额外的元素构造和拷贝操作很有用,特别是在容器中存储大型对象时,可以提高性能。
6. push_back
void push_back (const value_type& val);
是 std::list
容器的成员函数,用于在列表的末尾插入一个新元素。它接受一个常量引用作为参数,将传入的值插入到列表末尾。
示例用法:
std::list<int> myList;
myList.push_back(10);
myList.push_back(20);
myList.push_back(30);
在上面的例子中,push_back
函数分别将整数 10、20
和 30
插入到列表的末尾。
这个函数在操作上相对简单,但是可能涉及到内存分配和元素拷贝操作。如果插入的元素比较大,可能会导致额外的性能开销。
7. pop_back
void pop_back();
是 std::list
容器的成员函数,用于删除列表中的最后一个元素。它会将列表的最后一个元素从容器中移除,同时释放相应的内存资源。
示例用法:
std::list<int> myList;
myList.push_back(10);
myList.push_back(20);
myList.push_back(30);
myList.pop_back();
在上面的例子中,pop_back
函数会移除列表中的元素 30
,使列表变为 [10, 20]
。
需要注意的是,调用 pop_back
函数前需要确保列表不为空,否则会出现未定义的行为。可以通过 empty()
函数来判断列表是否为空。
8. emplace
template <class... Args> iterator emplace (const_iterator position, Args&&... args);
是 std::list
容器的成员函数,用于在指定位置插入一个新元素,并将元素构造函数的参数传递给插入的元素。
参数说明:
position
:要插入新元素的位置的迭代器。
args
:传递给新元素构造函数的参数。
该函数返回一个迭代器,指向新插入的元素。
示例用法:
std::list<int> myList = {10, 20, 30};
auto it = myList.begin();
++it; // 移动到第二个元素的位置
myList.emplace(it, 25); // 在第二个元素位置插入值为 25 的元素
在上面的例子中,emplace
函数在第二个元素的位置插入了一个值为 25
的新元素,使列表变为 [10, 25, 20, 30]
。返回的迭代器指向插入的元素 25
。
这个函数适用于在任意位置插入元素,并且可以通过参数直接传递给元素的构造函数。
9. insert
iterator insert (iterator position, const value_type& val);
void insert (iterator position, size_type n, const value_type& val);
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
iterator insert (iterator position, const value_type& val);
是 std::list
容器的成员函数,用于在指定位置插入一个新元素,新元素的值由 val
参数确定。
参数说明:
position
:要插入新元素的位置的迭代器。
val
:要插入的元素的值。
该函数返回一个迭代器,指向插入的元素。
示例用法:
std::list<int> myList = {10, 20, 30};
auto it = myList.begin();
++it; // 移动到第二个元素的位置
myList.insert(it, 25); // 在第二个元素位置插入值为 25 的元素
void insert (iterator position, size_type n, const value_type& val);
是另一个版本的插入函数,可以插入指定数量的相同值的元素。
参数说明:
position
:要插入新元素的位置的迭代器。
n
:要插入的相同元素的数量。
val
:要插入的元素的值。
template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last);
是另一个版本的插入函数,可以从指定范围的迭代器中插入一系列元素。
参数说明:
position
:要插入新元素的位置的迭代器。
first
和 last
:要插入的元素范围的迭代器。
这些 insert
函数提供了不同的插入方式,使你可以根据需要灵活地向列表中添加元素。
10. erase
iterator erase (iterator position);
和 iterator erase (iterator first, iterator last);
是 std::list
容器的成员函数,用于从列表中删除一个或多个元素。
iterator erase (iterator position);
删除指定位置的元素,并返回指向下一个元素的迭代器。
参数说明:
position
:要删除的元素的位置的迭代器。
返回值:指向被删除元素之后的元素的迭代器。
示例用法:
std::list<int> myList = {10, 20, 30, 40};
auto it = myList.begin();
++it; // 移动到第二个元素的位置
myList.erase(it); // 删除第二个元素
iterator erase (iterator first, iterator last);
删除指定范围内的元素,并返回指向被删除范围之后的元素的迭代器。
参数说明:
first
和 last
:要删除的元素范围的迭代器,删除的范围包括 first,但不包括 last。
返回值:指向被删除范围之后的元素的迭代器。
示例用法:
std::list<int> myList = {10, 20, 30, 40};
auto it1 = myList.begin();
auto it2 = myList.begin();
std::advance(it2, 2); // 移动到第三个元素的位置
myList.erase(it1, it2); // 删除第一个和第二个元素
这些函数允许你根据需要从列表中删除一个或多个元素,并返回正确的迭代器以便进行后续操作。
11. swap
void swap(list& x);
是 std::list
容器的成员函数,用于交换当前列表与另一个列表 x
的内容。
参数说明:
x
:要与当前列表进行内容交换的另一个列表。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList1 = {1, 2, 3};
std::list<int> myList2 = {4, 5, 6};
myList1.swap(myList2); // 交换两个列表的内容
std::cout << "myList1: ";
for (int num : myList1) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "myList2: ";
for (int num : myList2) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在上述示例中,myList1
和 myList2
的内容被交换,导致输出中显示的内容分别为 4 5 6
和 1 2 3
。这个函数对于在不同列表之间交换内容非常有用。
12. resize
void resize(size_type n, value_type val = value_type());
是 std::list
容器的成员函数,用于调整列表的大小。
参数说明:
n
:指定调整后的大小。
val
:在列表扩展时,用于填充新元素的值。默认值为 value_type()
,即类型的默认构造函数创建的值。
该函数通过在列表的末尾添加或删除元素,使列表的大小调整为指定的大小 n
。如果新的大小大于当前大小,新元素将用指定的值 val
填充。如果新的大小小于当前大小,多余的元素将被删除。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3, 4, 5};
myList.resize(3); // 调整列表大小为3
std::cout << "myList after resize to 3: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
myList.resize(5, 0); // 调整列表大小为5,并用0填充新元素
std::cout << "myList after resize to 5 with filling 0: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在上述示例中,首先调用 resize(3)
后,列表中只有前3个元素保留,然后调用 resize(5, 0)
后,列表中总共有5
个元素,其中新添加的2
个元素被填充为0
。
13. clear
void clear();
是 std::list
容器的成员函数,用于清空列表中的所有元素,使列表变为空列表。
该函数会删除列表中的所有元素,使列表变为空,但并不会释放列表所占用的内存空间,所以列表的容量不会变化。这可以有效地回收元素所占用的资源,但保留容量可以减少频繁的内存分配和释放操作,以提高性能。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3, 4, 5};
std::cout << "myList before clear: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
myList.clear(); // 清空列表
std::cout << "myList after clear: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在上述示例中,首先输出了清空前的列表元素,然后调用 clear()
后,列表中的所有元素被删除,输出了清空后的列表元素,此时列表为空。
list操作函数(Operations)
1. splice
void splice (iterator position, list& x);
该成员函数用于将另一个列表 x
中的所有元素移动到当前列表中,插入到指定位置 position
前。x
列表在移动后会变为空列表。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList1 = {1, 2, 3};
std::list<int> myList2 = {4, 5, 6};
auto it = myList1.begin();
std::advance(it, 2);
myList1.splice(it, myList2); // 将 myList2 的元素插入到 myList1 中
std::cout << "myList1 after splice: ";
for (int num : myList1) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "myList2 after splice: ";
for (int num : myList2) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
void splice (iterator position, list& x, iterator i);
该成员函数用于将另一个列表 x
中的元素移动到当前列表中,插入到指定位置 position
前,但只移动另一个列表 x
中的迭代器 i
指向的元素。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList1 = {1, 2, 3};
std::list<int> myList2 = {4, 5, 6};
auto it1 = myList1.begin();
std::advance(it1, 1);
auto it2 = myList2.begin();
myList1.splice(it1, myList2, it2); // 将 myList2 中的第一个元素插入到 myList1 中
std::cout << "myList1 after splice: ";
for (int num : myList1) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "myList2 after splice: ";
for (int num : myList2) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
void splice (iterator position, list& x, iterator first, iterator last);
该成员函数用于将另一个列表 x
中的一段元素范围 [first, last)
移动到当前列表中,插入到指定位置 position
前。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList1 = {1, 2, 3};
std::list<int> myList2 = {4, 5, 6};
auto it1 = myList1.begin();
std::advance(it1, 1);
auto it2_first = myList2.begin();
auto it2_last = myList2.begin();
std::advance(it2_last, 2);
myList1.splice(it1, myList2, it2_first, it2_last); // 将 myList2 中的前两个元素插入到 myList1 中
std::cout << "myList1 after splice: ";
for (int num : myList1) {
std::cout << num << " ";
}
std::cout << std::endl;
std::cout << "myList2 after splice: ";
for (int num : myList2) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
这些 splice
函数允许你在列表中移动元素,从一个列表中移动到另一个列表中,或在同一列表内重新排列元素的位置,而不需要进行元素的复制和删除。
2. remove
void remove (const value_type& val);
该成员函数用于从列表中移除所有等于给定值 val
的元素。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3, 2, 4, 2, 5};
myList.remove(2); // 移除列表中所有值为 2 的元素
std::cout << "myList after remove: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在示例中,myList
列表中的值为 2
的元素被移除,最终输出为 "1 3 4 5"
。
3. remove_if
template <class Predicate> void remove_if (Predicate pred);
这个成员函数用于根据给定的谓词函数 pred
移除满足特定条件的元素。
谓词函数 pred
接受一个参数并返回一个布尔值,用于判断是否需要移除该元素。如果谓词返回 true
,则该元素将被移除。
示例用法:
#include <iostream>
#include <list>
bool isEven(int num) {
return num % 2 == 0;
}
int main() {
std::list<int> myList = {1, 2, 3, 4, 5, 6, 7, 8, 9};
myList.remove_if(isEven); // 移除列表中所有偶数
std::cout << "myList after remove_if: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在示例中,myList
列表中的偶数元素被移除,最终输出为 "1 3 5 7 9"
。函数 isEven
是一个谓词函数,用于判断是否为偶数。
4. unique
void unique();
这个成员函数用于移除列表中相邻的重复元素。它只保留第一个出现的重复元素,移除后续的重复元素。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 2, 3, 4, 4, 4, 5};
myList.unique(); // 移除相邻的重复元素
std::cout << "myList after unique: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在示例中,myList
列表中的相邻重复元素被移除,最终输出为 "1 2 3 4 5"
。
template <class BinaryPredicate> void unique (BinaryPredicate binary_pred);
这个成员函数在移除相邻重复元素时,使用自定义的二元谓词函数 binary_pred
来判断是否为重复元素。该谓词函数接受两个参数,并返回一个布尔值,用于判断两个元素是否相等。
示例用法:
#include <iostream>
#include <list>
bool isEqual(int a, int b) {
return a == b;
}
int main() {
std::list<int> myList = {1, 2, 2, 3, 4, 4, 4, 5};
myList.unique(isEqual); // 使用 isEqual 判断是否为重复元素
std::cout << "myList after unique with custom predicate: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在示例中,通过自定义的谓词函数 isEqual
判断相邻的元素是否相等,移除相邻的重复元素,最终输出为 "1 2 3 4 5"
。
5. merge
void merge(list& x);
这个成员函数用于将另一个列表 x
合并到当前列表中,合并后的列表会按照升序排列。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList1 = {1, 3, 5};
std::list<int> myList2 = {2, 4, 6};
myList1.merge(myList2); // 将 myList2 合并到 myList1 中
std::cout << "myList1 after merge: ";
for (int num : myList1) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在示例中,myList2
列表被合并到了 myList1
中,合并后的列表按照升序排列,最终输出为 "1 2 3 4 5 6"
。
template <class Compare> void merge(list& x, Compare comp);
这个成员函数与上面的 merge
函数类似,但是它允许提供一个自定义的比较函数 comp 来决定合并后的顺序。
示例用法:
#include <iostream>
#include <list>
bool descendingOrder(int a, int b) {
return a > b;
}
int main() {
std::list<int> myList1 = {5, 3, 1};
std::list<int> myList2 = {6, 4, 2};
myList1.merge(myList2, descendingOrder); // 使用自定义比较函数合并
std::cout << "myList1 after merge with custom comparison: ";
for (int num : myList1) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在示例中,通过自定义的比较函数 descendingOrder
,将 myList2
列表合并到了 myList1
中,合并后的列表按照降序排列,最终输出为 "6 5 4 3 2 1"
。
6. sort
void sort();
这个成员函数用于对列表进行升序排序,默认使用 <
运算符进行比较。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {5, 3, 1, 4, 2};
myList.sort(); // 对列表进行升序排序
std::cout << "myList after sorting: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在示例中,列表中的元素经过排序后变为 "1 2 3 4 5"
。
template <class Compare> void sort(Compare comp);
这个成员函数与上面的 sort
函数类似,但是它允许提供一个自定义的比较函数 comp
来决定排序的顺序。
示例用法:
#include <iostream>
#include <list>
bool descendingOrder(int a, int b) {
return a > b;
}
int main() {
std::list<int> myList = {5, 3, 1, 4, 2};
myList.sort(descendingOrder); // 使用自定义比较函数进行降序排序
std::cout << "myList after custom sorting: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在示例中,通过自定义的比较函数 descendingOrder
,列表中的元素经过排序后变为 "5 4 3 2 1"
。
7. reverse
void reverse();
函数用于将列表中的元素逆序排列。
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3, 4, 5};
myList.reverse(); // 将列表中的元素逆序排列
std::cout << "myList after reversing: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
在示例中,列表中的元素经过逆序排列后变为 "5 4 3 2 1"
。
list的迭代器失效
迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
当使用 std::list
进行删除操作时,可能会导致迭代器失效。下面是一个示例:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3, 4, 5};
auto it = myList.begin();
++it; // Move the iterator to the second element
myList.erase(it); // Erase the second element
for (auto num : myList) {
std::cout << num << " ";
}
return 0;
}
在上面的示例中,当我们在第二个元素位置处使用 erase
函数删除元素后,迭代器 it
就会失效,因为它指向的元素已经被删除。如果我们尝试使用失效的迭代器,可能会导致未定义的行为。
要修正这个问题,可以使用 erase
函数的返回值,它会返回一个指向下一个有效元素的迭代器:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3, 4, 5};
auto it = myList.begin();
++it; // Move the iterator to the second element
it = myList.erase(it); // Erase the second element and update the iterator
for (auto num : myList) {
std::cout << num << " ";
}
return 0;
}
在这个修正后的示例中,我们使用 erase
函数的返回值更新了迭代器 it
,以确保它指向的是有效的元素。这样就避免了使用失效迭代器引发的问题。
结语
有兴趣的小伙伴可以关注作者,如果觉得内容不错,请给个一键三连吧,蟹蟹你哟!!!
制作不易,如有不正之处敬请指出
感谢大家的来访,UU们的观看是我坚持下去的动力
在时间的催化剂下,让我们彼此都成为更优秀的人吧!!!