C++初阶之一篇文章教会你list(理解和使用)
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 insertstd::cout << "List contents:" << std::endl;for (const auto& num : myList) {std::cout << num << " ";}std::cout << std::endl;myList.push_front(25); // Move insertstd::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); // 调整列表大小为3std::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 elementmyList.erase(it); // Erase the second elementfor (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 elementit = myList.erase(it); // Erase the second element and update the iteratorfor (auto num : myList) {std::cout << num << " ";}return 0;
}
在这个修正后的示例中,我们使用 erase
函数的返回值更新了迭代器 it
,以确保它指向的是有效的元素。这样就避免了使用失效迭代器引发的问题。
结语
有兴趣的小伙伴可以关注作者,如果觉得内容不错,请给个一键三连吧,蟹蟹你哟!!!
制作不易,如有不正之处敬请指出
感谢大家的来访,UU们的观看是我坚持下去的动力
在时间的催化剂下,让我们彼此都成为更优秀的人吧!!!
相关文章:

C++初阶之一篇文章教会你list(理解和使用)
list(理解和使用) 什么是list特点和优势基本操作示例用法与其他序列式容器(如 std::vector 和 std::deque)相比,std::list 显著的区别和优势成员类型 list构造函数1. default (1)2. fill (2)3.range (3)4. copy (4) li…...

如何给Linux开启swap虚拟内存
查看系统内存资源 free -h 创建swap分区 dd if/dev/zero of/swapfile bs1024 count4194304dev/zero:是Linux的一种特殊字符设备(输入设备),可以用来创建一个指定长度用于初始化的空文件,如临时交换文件,该设备无穷尽地提供0&…...

spring按条件注入@Condition及springboot对其的扩展
概述 spring的ioc极大的方便了日常开发,但随着业务的迭代。配置的一些参数在某些情况下需要按条件注入。 比如原先定义的db公共模块下,相关的配置和工具类只是基于mysql的。但是后续有模块需要使用mongo/es等其他数据库,又想继续使用db公共…...

MySQL多表连接查询3
目录 表结构 创建表 表数据 查询需求: 1.查询student表的所有记录 2.查询student表的第2条到4条记录 3.从student表查询所有学生的学号(id)、姓名(name)和院系(department)的信息 4.从s…...

【从零开始学习JAVA | 第四十五篇】反射
目录 前言: 反射: 使用反射的步骤: 1.获取阶段: 2.使用阶段: 反射的应用场景: 使用反射的优缺点: 总结: 前言: Java中的反射是一项强大而灵活的功能࿰…...
顺丰科技数据治理实践
01 顺丰数据治理体系演进路线 顺丰做数据治理十多年,数据治理体系的模块是逐步来建设的。十年前,我们就已经建了数仓,同步做了元数据管理,数据质量管理,以及数据安全的管理。顺丰数据治理的演进路线分 3 个阶段。 第…...

Nginx+Tomcat负载均衡、动静分离实例详细部署
一、反向代理两种模式 四层反向代理 基于四层的iptcp/upd端口的代理 他是http块同一级,一般配置在http块上面。 他是需要用到stream模块的,一般四层里面没有自带,需要编译安装一下。并在stream模块里面添加upstream 服务器名称,…...

Java多线程(3)---锁策略、CAS和JUC
目录 前言 一.锁策略 1.1乐观锁和悲观锁 ⭐ 两者的概念 ⭐实现方法 1.2读写锁 ⭐概念 ⭐实现方法 1.3重量级锁和轻量级锁 1.4自旋锁和挂起等待锁 ⭐概念 ⭐代码实现 1.5公平锁和非公平锁 1.6可重入锁和不可重入锁 二.CAS 2.1为什么需要CAS 2.2CAS是什么 ⭐CAS…...

Linux:Shell编辑之文本处理器(awk)
目录 绪论 1、用法 1.1 格式选项 1.2 awk 常用内置变量 1.3 awk的打印功能 1.4 奇偶打印 1.5 awk运算 1.6 awk的内置函数:getline 1.7 文本过滤打印 1.8 awk条件判断打印 1.9 三元表达式,类似于java 1.10 awk的精确筛选 1.11 awk和tr比较改变…...

探索FSM (有限状态机)应用
有限状态机(FSM) 是计算机科学中的一种数学模型,可用于表示和控制系统的行为。它由一组状态以及定义在这些状态上的转换函数组成。FSM 被广泛用于计算机程序中的状态机制。 有限状态机(FSM)应用场景 在各种自动化系统…...
6.continue break
6.1continue 关键字 continue 关键字用于立即跳出本次循环,继续下一次循环(本次循环体continue之后的代码会少执行一次)。 例如:吃5个包子,第3个有虫子,就扔掉第3个包子,继续吃第4个第5个包子…...
如何在Linux中强制关闭卡住的PyCharm
在使用PyCharm进行Python开发时,有时可能会遇到卡顿或无响应的情况。当PyCharm卡住时,我们需要强制关闭它以恢复正常操作。今天,我们将介绍在Linux系统中如何强制关闭PyCharm的几种方法。 1. 使用键盘快捷键 在PyCharm所在的窗口中…...
c# Excel数据的导出与导入
搬运:Datagrideview 数据导出Excel , Exel数据导入 //------------------------------------------------------------------------------------- // All Rights Reserved , Copyright (C) 2013 , DZD , Ltd . //----------------------------------------------------------…...

Kotlin~Mediator中介者模式
概念 创建一个中介来降低对象之间的耦合度,关系”多对多“变为“一对多”。 角色介绍 Mediator:抽象中介者,接口或者抽象类。ConcreteMediator:中介者具体实现,实现中介者接口,定义一个List管理Colleagu…...
石子合并问题
一.试题 在一个园形操场的四周摆放N堆石子(N≤100),现要将石子有次序地合并成一堆。规定 每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。 编一程序,由文件读入…...
剑指Offer-搜索与回溯算法
文章目录 剑指 Offer 32 - I. 从上到下打印二叉树题意:解:代码: 剑指 Offer 32 - II. 从上到下打印二叉树 II题意:解:代码: 剑指 Offer 32 - III. 从上到下打印二叉树 III题意:解:代…...

【云原生】Docker 详解(三):Docker 镜像管理基础
Docker 详解(三):Docker 镜像管理基础 1.镜像的概念 镜像可以理解为应用程序的集装箱,而 Docker 用来装卸集装箱。 Docker 镜像含有启动容器所需要的文件系统及其内容,因此,其用于创建并启动容器。 Dock…...

SD-MTSP:蜘蛛蜂优化算法SWO求解单仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)
一、蜘蛛蜂优化算法SWO 蜘蛛蜂优化算法(Spider wasp optimizer,SWO)由Mohamed Abdel-Basset等人于2023年提出,该算法模型雌性蜘蛛蜂的狩猎、筑巢和交配行为,具有搜索速度快,求解精度高的优势。蜘蛛蜂优化算…...
【ARM 嵌入式 编译系列 3.1 -- GCC __attribute__((used)) 使用】
文章目录 __attribute__((used)) 属性介绍代码演示编译与输出GCC 编译选项 上篇文章:ARM 嵌入式 编译系列 3 – GCC attribute((weak)) 弱符号使用 下篇文章:ARM 嵌入式 编译系列 3.2 – glibc 学习 __attribute__((used)) 属性介绍 在普通的 C/C 程序中…...

C++ ModBUS TCP客户端工具 qModMaster 介绍及使用
qModMaster工具介绍 QModMaster是一个基于Qt的Modbus主站(Master)模拟器,用于模拟和测试Modbus TCP和RTU通信。它提供了一个直观的图形界面,使用户能够轻松设置和发送Modbus请求,并查看和分析响应数据。 以下是QModM…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...