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::vectorstd::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

assignstd::list 容器的成员函数,用于将容器的内容替换为新的元素。这个函数有三个不同版本:

使用迭代器范围:

template <class InputIterator>
void assign (InputIterator first, InputIterator last);

这个版本接受两个迭代器参数 firstlast,用来指定一个范围。它会将容器的内容替换为范围 [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_frontstd::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 的结构体,它有两个成员变量:nameage。然后我们创建了一个空的 std::list 容器 personList,使用 emplace_front 函数分别在容器的开头插入了几个新的 Person 对象,直接在插入位置进行构造。

注意,emplace_front 的参数被传递给 Person 类型的构造函数,用于构造新的 Person 对象。这样做避免了额外的拷贝或移动操作,提高了效率。

3. push_front

push_frontstd::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、2030 插入到列表的末尾。

这个函数在操作上相对简单,但是可能涉及到内存分配和元素拷贝操作。如果插入的元素比较大,可能会导致额外的性能开销。

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:要插入新元素的位置的迭代器。
firstlast:要插入的元素范围的迭代器。

这些 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); 删除指定范围内的元素,并返回指向被删除范围之后的元素的迭代器。

参数说明:

firstlast:要删除的元素范围的迭代器,删除的范围包括 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;
}

在上述示例中,myList1myList2 的内容被交换,导致输出中显示的内容分别为 4 5 61 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们的观看是我坚持下去的动力
在时间的催化剂下,让我们彼此都成为更优秀的人吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/70172.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu20.04磁盘满了 /dev/mapper/ubuntu--vg-ubuntu--lv 占用 100%

问题 执行 mysql 大文件导入任务&#xff0c;最后快完成了&#xff0c;查看结果发现错了&#xff01;悲催&#xff01;都执行了 两天了 The table ‘XXXXXX’ is full &#xff1f; 磁盘满了&#xff1f; 刚好之前另一个 centos 服务器上也出现过磁盘满了&#xff0c;因此&a…

什么是Selenium?使用Selenium进行自动化测试

什么是 Selenium&#xff1f;   Selenium 是一种开源工具&#xff0c;用于在 Web 浏览器上执行自动化测试&#xff08;使用任何 Web 浏览器进行 Web 应用程序测试&#xff09;。   等等&#xff0c;先别激动&#xff0c;让我再次重申一下&#xff0c;Selenium 仅可以测试We…

大连交通大学813软件工程考研习题

1.什么是软件生存周期模型?有哪些主要模型? 生存周期模型&#xff1a;描述软件开发过程中各种活动如何执行的模型。对软件开发提供强有力的支持&#xff0c;为开发过程中的活动提供统一的政策保证&#xff0c;为参与开发的人员提供帮助和指导&#xff0c;是软件生存周期模型…

云计算——常见存储类型

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 目录 前言 一.存储类型 1.本地磁盘 2.DAS 3.NAS 4.SAN &#xff08;1&#xff09;FC SA…

锁定Mac的内置键盘,防止外接键盘时的误触

场景&#xff1a;把你的外接键盘放在mac上&#xff0c;然后打字时&#xff0c;发现外接键盘误触mac键盘&#xff0c;导致使用体验极差 解决方案&#xff1a;下载Karabiner-Elements这款软件&#xff0c;并给它开启相关权限。 地址&#xff1a;https://github.com/pqrs-org/Ka…

ModaHub魔搭社区——Milvus 、Qdrant、Waeviate、Pinecone、ElasticSearch矢量数据库对比

资本市场上,2022年也是风起云涌的一年的,各大向量数据库公司纷纷完成了千万美元级别新一轮的融资。可以预见,2023年将会是向量数据库继续快速发展的一年,也会是这一新兴技术由发展走向成熟的一年。这里针对Milvus 、Qdrant、Waeviate、Pinecone、ElasticSearch这五个流行的…

编写简单的.gitlab-ci.yml打包部署项目

服务器说明&#xff1a; 192.168.192.120&#xff1a;项目服务器 192.168.192.121&#xff1a;GitLab 为了可以使用gitlab的cicd功能&#xff0c;我们需要先安装GitLab Runner 安装GitLab Runner参考&#xff1a; GitLab实现CICD自动化部署_gitlab cidi_程序员xiaoQ的博客-CS…

HCIA---TCP/UDP协议

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目录 前言 一.UDP协议简介 UDP协议的特点&#xff1a; 二.TCP协议简介 TCP协议特点 三.TCP和UDP的区别 四.TCP/IP结构详解 五.TCP运输连接的阶段 ​编辑 …

43..利用fsolve函数解对应lambda下的方程组(matlab程序)

1.简述 fsolve的基本用法 : x fsolve(fun,x0) 其中fun应为函数句柄&#xff0c;x0为搜索的种子&#xff0c;即预估的fun0的解的大致位置。 函数句柄的定义方式主要有两种&#xff1a; 1.定义函数文件&#xff0c;使用操作符 定义function文件root2d.m, 如下&#xff1a; …

怎么把图片表格转换成word表格?几个步骤达成

在处理文档时&#xff0c;图片表格的转换是一个常见的需求。而手动输入表格是非常耗时的&#xff0c;因此&#xff0c;使用文本识别软件来自动转换图片表格可以大大提高工作效率。在本文中&#xff0c;我们将介绍如何使用OCR文字识别技术来将图片表格转换为Word表格。 OCR文字识…

yolov2检测网数据集标注_labelme使用_json2txt格式转换

yolov2检测网数据集标注_labelme使用_json2txt格式转换 一、安装Anaconda二、创建labelme虚拟环境三、使用labelme标注健康非健康猫狗数据3.1 打开数据集所在文件夹3.2 进行标注数据集3.3 json2txt3.4 按文件目录和训练测试数据集重分配 四、数据喂给服务器网络参考链接 一、安…

Paddle OCR V4 测试Demo

效果 项目 VS2022.net4.8OCRV4 代码 using OpenCvSharp; using Sdcb.PaddleInference; using Sdcb.PaddleOCR; using Sdcb.PaddleOCR.Models; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; usin…

[保研/考研机试] KY135 又一版 A+B 浙江大学复试上机题 C++实现

题目链接&#xff1a; KY135 又一版 AB https://www.nowcoder.com/share/jump/437195121691736185698 描述 输入两个不超过整型定义的非负10进制整数A和B(<231-1)&#xff0c;输出AB的m (1 < m <10)进制数。 输入描述&#xff1a; 输入格式&#xff1a;测试输入包…

C语言的简单基础知识

C语言的基础知识包括变量、数据类型、运算符、控制流语句、函数等。下面会对每个部分进行详细解释&#xff0c;并给出相应的案例。 变量和数据类型&#xff1a; 变量&#xff1a;C语言中的变量用于存储数据&#xff0c;并且需要先声明后使用。声明变量时需要指定其数据类型。例…

包管理工具详解npm 、 yarn 、 cnpm 、 npx 、 pnpm(2023)

1、包管理工具npm &#xff08;1&#xff09;包管理工具npm&#xff1a; Node Package Manager&#xff0c;也就是Node包管理器&#xff1b;但是目前已经不仅仅是Node包管理器了&#xff0c;在前端项目中我们也在使用它来管理依赖的包&#xff1b;比如vue、vue-router、vuex、…

怎么用PS的魔术棒抠图?PS魔术棒抠图的操作方法

使用PS的魔术棒抠图教程&#xff1a; 1、首先&#xff0c;在ps界面上方点击“文件”选项&#xff0c;再在其弹出的选项栏中选择“打开”选项。然后&#xff0c;打开你所需要的图片。 2、然后&#xff0c;单击左侧的“魔术棒”工具。 3、然后&#xff0c;用鼠标点击图片的背景&…

根据渲染数据长度动态渲染后缀图标

在动态获取数据时&#xff0c;想要渲染后面的图标是根据数据的长度渲染图标位置&#xff0c;效果如下&#xff1a; 代码如下&#xff1a; <el-row :gutter"60"><el-col :span"24"><el-form-item><el-input v-model.trim"form…

你不能访问此共享文件夹因为你组织的安全策略

我在windows 10中尝试访问没有密码的共享文件只报错如下&#xff1a; 解决办法 运气中执行 gpedit.msc来启动本地组策略编辑器。 这样设置完成后&#xff0c;就可以在运行中输入 \\192.168.199.1\可以访问共享的文件了。 参考 https://zhuanlan.zhihu.com/p/164721714…

从零构建深度学习推理框架-7 计算图的表达式

什么是表达式 表达式就是一个计算过程,类似于如下&#xff1a; output_mid input1 input2 output output_mid * input3用图形来表达就是这样的。 但是在PNNX的表达式&#xff08;Experssion Layer&#xff09;中不是这个样子&#xff0c;而是以一种抽象得方式&#xff0c;…

韩顺平Linux基础篇

一、课程内容 二、Linux应用领域 一、Linux使用在哪些地方 Linux最强应用&#xff1a;服务器 三、Linux概述 三、Linux和Unix的关系 五、VM和Linux的安装 基本说明 学习Linux需要一个环境&#xff0c;我们需要创建一个虚拟机&#xff0c;然后再虚拟机上安装一个Centos系统来学…