C++初阶之一篇文章教会你list(理解和使用)

news2024/11/26 8:53:15

在这里插入图片描述

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们的观看是我坚持下去的动力
在时间的催化剂下,让我们彼此都成为更优秀的人吧!!!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无涯教程-Perl - opendir函数

描述 此函数使用readdir函数打开目录EXPR,并将其与DIRHANDLE关联以进行处理。 语法 以下是此函数的简单语法- opendir DIRHANDLE, EXPR返回值 如果成功,此函数将返回true。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl -w$dirname"/tmp";opendir ( …

MySQL~事务的四大特性和隔离级别

事务的四大特性 1.原子性&#xff1a;一个事务&#xff08;transaction&#xff09;中的所有操作&#xff0c;要么全部完成&#xff0c;要么全部不完成。事务在执行过程中发生错误&#xff0c;会被回滚&#xff08;Rollback&#xff09;到事务开始前的状态&#xff0c;就像这个…

MySQL环境与配置

安装MySQL https://www.mysql.com/ 进入下载官网后 下载完成后运行安装包下载完成后运行安装包 选择完路径后一直点下一步 然后运行MySQL 设置密码 这里如安装失败请右键我的电脑点击属性&#xff0c;检查电脑名和组名是否为英文 启动与停止MySQL mysql安装完毕后默认是运行…

【C语言】进阶指针,超详解,含丰富代码示例

文章目录 前言指针进阶的重点内容1.字符指针2.数组指针3.指针数组4.函数指针5.函数指针数组6. 指向函数指针数组的指针 总结 这里是初阶的链接&#xff0c;方便大家对照查看&#xff01;&#xff01;&#xff01;添加链接描述 前言 大家好呀&#xff0c;今天和大家将指针进阶…

SpringBoot在线失物招领系统

一个基于SpringBootSemanticUI的pc Web在线失物招领系统 http://localhost:8080/swzl/index 主页 http://localhost:8080/swzl/login 登录页 用户表user admin字段为true是管理员 false用户 springboot2.3 springmvc mybatis html ajax idea 或eclipse maven mys…

9.3.2.1网络原理(UDP)

1.UDP的基本特点:无连接,不可靠传输,面向数据报,全双工. 2.1~1024的端口号有特定的含义,不建议使用.比如21:ftp,22:ssh,80:http,443:https. 3.CRC校验算法:循环冗余校验和,把UDP报中的每个字节都依次进行累加,把累加的结果,放到两个字节的变量中,溢出也无所谓,因为都加了一遍.…

机器学习笔记 - 关于向量嵌入​embedding在机器学习中的使用

向量嵌入概述 向量嵌入是机器学习中最有趣和最有用的概念之一。它们是许多 NLP、推荐和搜索算法的核心。如果您曾经使用过推荐引擎、语音助手、语言翻译器等工具,您就会遇到过依赖嵌入的系统。 与大多数软件算法一样,机器学习算法也需要使用数字。有时,我们的数据集包含数值…

Centos操作系统新安装的Python3中安装mysqlclient库

问题简介&#xff1a; mysqlclient 是python中的一个连接MySQL数据库的重要的三方库&#xff0c;但是在centos中使用pip3 install mysqlclient 方法安装一直报错&#xff0c;经过两天时间的排查,终于找到了解决问题的方法。 [rootd3acd2b8211d /]# pip3 install mysqlclient Co…

聊聊51单片机

目录 1.介绍 2.发展 3.应用领域 4.发展前景 1.介绍 51单片机&#xff08;AT89C51&#xff09;是一种常见的8位微控制器&#xff0c;属于Intel MCS-51系列。它是一种低功耗、高性能的单片机&#xff0c;广泛应用于嵌入式系统中。 51单片机具有很多特点和功能&#xff0c;例如…

用js快速生成一个简单的css原子库 例如: .mr-18 .pl-18

第三方css原子库的缺点 比如 tailwindcss&#xff0c;有学习成本最开始写的时候效率可能还没有我们自己手写效率高&#xff0c;需要配置&#xff0c;会有原始样式被覆盖的问题&#xff1b;总之就是一个字重 自己搓的优点 学习成本低灵活不会有副作用 <!DOCTYPE html>…

eclipse 导入项目js报错问题

eclipse 导入项目后会出现项目中的js文件报错&#xff08;红叉&#xff09;&#xff0c;如下图所示&#xff0c;有时候报错的文件很多&#xff0c;需要集中处理。 解决办法&#xff1a; 右键项目名称》Properties》MyEclipse》JavaScript》Include Path&#xff0c;在右侧选择“…

C语言案例 将一个数组逆序输出14

题目&#xff1a;将一个数组逆序输出 程序分析 数组是根据下标进行输出的&#xff0c;因此&#xff0c;我们可以逆向下标顺序输出。 步骤一&#xff1a;定义程序目标 编写C程序&#xff0c;输入数组元素&#xff0c;然后进行逆序输出。 步骤 二&#xff1a;程序设计 整个程序使…

国产航顺HK32F030M: 基于NTC负温度系数的温度计

前言&#xff1a; 家里的一个儿童澡盆附带的温度计坏掉了&#xff0c;拆解后发现这东西做的真垃圾&#xff01;索性自己做一个。拆下了里面的NTC热敏电阻&#xff0c;但是不知道NTC的性能参数&#xff0c;经过测量与查资料后&#xff0c;采用用中位值滤波 、 Steinhart-Hart方…

期权的希腊字母计算及应用

1.前言 期权价格会受到多个因素的影响,如标的资产价格、时间、波动率等,所以引入希腊字母来衡量不同因素对期权价格的影响程度,即Delta、Gamma、Vega、Theta和Rho,从而更综合的研究期权的影响因素。对于单独希腊值的作用,Delta经常被用做计算期权交易杠杆的指标、期权和期…

Linux系统下Redis3.2集群

本节主要学习reids主从复制的概念&#xff0c;作用&#xff0c;缺点&#xff0c;流程&#xff0c;搭建&#xff0c;验证&#xff0c;reids哨兵模式的概念&#xff0c;作用&#xff0c;缺点&#xff0c;结构&#xff0c;搭建&#xff0c;验证等。 文章目录 一、redis主从复制 …

【网络编程(三)】Netty入门到实战这一篇就够了

Netty入门到实战 NettyNetty 核心组件和流程分析数据流转的底层核心ByteBuf 原理分析Netty 解决半包、粘包问题Netty 实现心跳检查机制 Netty NIO 是一种 I/O 模型&#xff0c;netty 是基于 NIO 开发出来的一款异步事件驱动框架&#xff0c;它是一个通用的网络应用程序框架。n…

Transformer(二)(VIT,TNT)(基于视觉CV)

目录 1.视觉中的Attention 2.VIT框架&#xff08;图像分类&#xff0c;不需要decoder&#xff09; 2.1整体框架 2.2.CNN和Transformer遇到的问题 2.3.1CNN 2.3.2Transformer 2.3.3二者对比 2.4.公式理解 3TNT 参考文献 1.视觉中的Attention 对于人类而言看到一幅图可以立…

面向对象编程的特征:抽象、封装、继承和多态

文章目录 1. 抽象和封装&#xff1a;揭示事物本质&#xff0c;隐藏细节抽象封装 2. 继承&#xff1a;代码复用&#xff0c;提高可维护性3. 多态&#xff1a;灵活应对不同情境总结 面向对象编程&#xff08;Object-Oriented Programming&#xff0c;OOP&#xff09;是一种现代的…

感应电机的谐波分析 - Part2-1理论推演

金属腔体生成电磁波后&#xff0c;这些电磁波会像黑体辐射一样无论反射多少次&#xff0c;终将会被壳体吸收&#xff1f; 根据热力学的基本原理&#xff0c;当金属腔体内的电磁波频率和温度满足一定条件时&#xff0c;电磁波会像黑体辐射一样被吸收。这种现象被称为热平衡。一旦…

mybatis(上)

mybatis&#xff1a;原来是apache的一个开源项目&#xff0c;叫ibatis。2010年转移谷歌&#xff0c;从3.0开始改名为mybatis mybatis是一款优秀的持久层框架&#xff0c;是对jdbc功能进行轻量级的封装&#xff0c;提供了统一的数据库信息配置统一放在一个xml文件中&#xff0c;…