C++中list的使用

文章目录

  • 一、 list简介
  • 二、 构造函数
    • 1. 默认构造函数
    • 2. 拷贝构造
    • 3. 迭代器区间初始化
    • 4. 插入n个值为x的数据
    • 5. 代码示例
  • 三、 容量和元素访问
    • 1. empty()
    • 2. size()
    • 3. max_size()
    • 3. back()
    • 4. front()
    • 5. 代码示例
  • 四、 增删查改
    • 1. push_back()
    • 2. push_front()
    • 3. emplace_back()
    • 4. emplace_front()
    • 5. pop_back()
    • 6. pop_front()
    • 7. insert()
    • 8. erase()
    • 9. clear()
    • 10. swap
  • 五、迭代器
    • 1. begin()
    • 2. end()
    • 3. rbegin()
    • 4. rend()
    • 5. cbegin()、cend()、crbegin()、crend()
  • 六、 操作函数
    • 1. splice()
    • 2. unique()
    • 3. remove()
    • 4. remove_if()
    • 5. merge()
    • 6. sort()
    • 7 . reserve()
  • 七、 sort排序效率测评
  • 八、 list迭代器失效问题


一、 list简介

C++ STL(Standard Template Library)中的list是一个双向链表容器,它允许在序列中的任何位置进行快速插入和删除操作。list容器中的元素不是连续存储的,而是由节点(通常包含数据和指向下一个及前一个元素的指针)组成,因此它不需要在插入或删除元素时移动其他元素。

以下是list的一些基本特点和用法:

特点

  1. 双向链表:每个元素都包含指向其前一个和后一个元素的指针,这使得在任何位置进行插入和删除操作都非常高效。
  2. 动态大小list的大小可以动态变化,可以根据需要添加或删除元素。
  3. 非连续存储:与数组或向量不同,list的元素不是连续存储在内存中的。

基本用法

包含头文件

使用list需要包含<list>头文件。

#include <list>

创建list

可以创建一个空的list,也可以在创建时初始化它。

std::list<int> myList; // 创建一个空的int类型的list
std::list<int> myList = {1, 2, 3, 4, 5}; // 创建一个包含5个元素的list

添加元素

可以使用push_backpush_frontinsert等方法向list中添加元素。

myList.push_back(6); // 在list的末尾添加元素6
myList.push_front(0); // 在list的开头添加元素0
myList.insert(myList.begin(), 7); // 在list的开头插入元素7

删除元素

可以使用pop_backpop_fronterase等方法从list中删除元素。

myList.pop_back(); // 删除list的最后一个元素
myList.pop_front(); // 删除list的第一个元素
myList.erase(myList.begin()); // 删除list的第一个元素(使用迭代器)

遍历元素

可以使用迭代器或范围基于的for循环来遍历list中的元素。

// 使用迭代器遍历
for (std::list<int>::iterator it = myList.begin(); it != myList.end(); ++it) {
    std::cout << *it << " ";
}

// 使用范围基于的for循环遍历
for (const auto& elem : myList) {
    std::cout << elem << " ";
}

查找元素

可以使用find方法来查找list中的元素。

auto it = std::find(myList.begin(), myList.end(), 3); // 查找值为3的元素
if (it != myList.end()) {
    std::cout << "Found element: " << *it << std::endl;
} else {
    std::cout << "Element not found" << std::endl;
}

这只是list容器的一些基本用法,它还提供了许多其他功能和方法,如排序、合并、分割等,可以通过查阅C++ STL文档来了解更多详情。

二、 构造函数

在这里插入图片描述

构造函数( (constructor))接口说明
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

1. 默认构造函数

这个构造函数用于创建一个空的 std::list 容器。它可以接受一个可选的分配器参数,用于指定内存分配策略。

list() //默认构造函数
std::list<int> ls; //建立一个空的list容器

2. 拷贝构造

拷贝构造用于创建一个与已存在的std::list容器x相同的副本,它会将 x中的所有元素拷贝到新的容器中。

list (const list& x) //拷贝构造
std::list<int> A={1,2,3,4};
std::list<int> copy(A);

3. 迭代器区间初始化

这个构造函数使用迭代器范围 [first, last) 中的元素创建一个 std::list 容器。这使你可以通过一个迭代器范围来初始化容器。同样,它也接受一个可选的分配器参数。

list (InputIterator first, InputIterator last) //用[first, last)区间中的元素构造list
std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<int> List(vec.begin(), vec.end());
 // 从迭代器范围内的元素创建 std::list 容器

4. 插入n个值为x的数据

这个构造函数用于创建一个包含 n 个元素的 std::list 容器,并将这些元素初始化为 val。你可以通过传递不同的 val 值来创建一个包含相同值的容器。同样,也可以传递一个可选的分配器参数。

list (size_type n, const value_type& val = value_type()) //构造的list中包含n个值为val的元素
std::list<int> A(5,10);  //构建一个有五个元素,每个元素都是10的容器

5. 代码示例

#include <iostream>
#include <list>

int main()
{
    std::list<int> first;                                // 默认构造函数
    std::list<int> second(4, 100);                       // 四个元素都是100
    std::list<int> third(second.begin(), second.end());  // 迭代器区间构造
    std::list<int> fourth(third);                       //拷贝构造

    //迭代器构造函数也可以用于从数组构造:
    int myints[] = { 16,2,77,29 };
    std::list<int> fifth(myints, myints + sizeof(myints) / sizeof(int));

    std::cout << "The contents of fifth are: ";
    for (std::list<int>::iterator it = fifth.begin(); it != fifth.end(); it++)
        std::cout << *it << ' ';

    std::cout << '\n';

    return 0;
}

在这里插入图片描述

三、 容量和元素访问

1. empty()

在这里插入图片描述

判断容器中元素是否为空,为空返回true否则false。

2. size()

在这里插入图片描述

返回容器中元素个数。

3. max_size()

在这里插入图片描述

返回列表容器可以容纳的最大元素数。

3. back()

在这里插入图片描述

返回对列表容器中最后一个元素的引用。
与成员list::end不同,该函数返回的迭代器刚好经过该元素,它返回的是直接引用。
在空容器上调用此函数会导致未定义的行为。

4. front()

在这里插入图片描述

返回对列表容器中第一个元素的引用。
与向同一元素返回迭代器的成员list::begin不同,此函数返回一个直接引用。
在空容器上调用此函数会导致未定义的行为。

5. 代码示例

#include <iostream>
#include <list>

int main()
{
    std::list<int> Mylist;
    Mylist.push_back(1);//在最后插入一个元素,后面会详细说明
    Mylist.push_back(2);
    Mylist.push_back(3);
    Mylist.push_back(4);
    std::cout << "Mylist中元素个数为:" << Mylist.size() << std::endl;
    std::cout << "Mylist是否为空:" << Mylist.empty() << std::endl;
    std::cout << "Mylist首元素为:" << Mylist.front() << std::endl;
    std::cout << "Mylist最后一个元素为:" << Mylist.back() << std::endl;
    return 0;
}

在这里插入图片描述

四、 增删查改

1. push_back()

在这里插入图片描述
在列表容器的末尾,在其当前最后一个元素之后添加一个新元素。

#include <iostream>
#include <list>

int main()
{
    std::list<int> Mylist;
    Mylist.push_back(1);
    Mylist.push_back(2);
    //输出list中的元素
    for (const auto e : Mylist)
        std::cout << e << " ";
    return 0;
}

在这里插入图片描述

2. push_front()

在这里插入图片描述

在列表的开头,即当前第一个元素之前插入一个新元素。

#include <iostream>
#include <list>

int main()
{
    std::list<int> Mylist;
    Mylist.push_back(1);
    Mylist.push_back(2);
    //输出list中的元素
    for (const auto e : Mylist)
        std::cout << e << " ";
    std::cout << std::endl;

    Mylist.push_front(3);
    Mylist.push_front(4);
    for (const auto e : Mylist)
        std::cout << e << " ";
    std::cout << std::endl;
    return 0;
}

在这里插入图片描述

3. emplace_back()

在这里插入图片描述
用法和push_back一样,作用也一样,就是在特定情况下效率比push_back高
这里不详细介绍他的原理了,后面会专门写一篇有关右值引用的博客,在内篇文章中我们会探讨这个问题。

  1. 在列表末尾的当前最后一个元素的正后方插入一个新元素。这个新元素是使用args作为其构造参数就地构造的。
  2. 该元素是通过调用allocator_traits::construct和转发的args就地构造的。
  3. 存在一个类似的成员函数push_back,它可以将现有对象复制或移动到容器中

在这里插入图片描述

4. emplace_front()

在这里插入图片描述
用法和push_front一样,作用也一样,就是在特定情况下效率比push_front高
这里不详细介绍他的原理了,后面会专门写一篇有关右值引用的博客,在内篇文章中我们会探讨这个问题。

  1. 在列表的开头,即当前第一个元素之前插入一个新元素。这个新元素是使用args作为其构造参数就地构造的。
  2. 该元素是通过调用allocator_traits::construct和转发的args就地构造的。
  3. 存在一个类似的成员函数push_front,它将现有对象复制或移动到容器中。

在这里插入图片描述

5. pop_back()

在这里插入图片描述

删除list容器中的最后一个元素。

#include <iostream>
#include <list>

int main()
{
    std::list<int> Mylist;
    Mylist.push_back(1);
    Mylist.push_back(2);
    Mylist.push_back(3);
    Mylist.push_back(4);
    Mylist.pop_back();
    //输出list中的元素
    for (const auto e : Mylist)
        std::cout << e << " ";
    std::cout << std::endl;

    return 0;
}

在这里插入图片描述

6. pop_front()

在这里插入图片描述

删除list容器中的第一个元素

#include <iostream>
#include <list>
int main()
{
    std::list<int> Mylist;
    Mylist.push_back(1);
    Mylist.push_back(2);
    Mylist.push_back(3);
    Mylist.push_back(4);
    Mylist.pop_front();
    Mylist.pop_front();
    //输出list中的元素
    for (const auto e : Mylist)
        std::cout << e << " ";
    std::cout << std::endl;
    return 0;
}

在这里插入图片描述

7. insert()

在这里插入图片描述

  1. 通过在指定位置的元素之前插入新元素来扩展容器。这有效地将list大小增加了插入的元素数量。
  2. 与其他标准序列容器不同,list和forward_list对象经过专门设计,可以有效地在任何位置插入和删除元素,即使是在序列的中间。
  3. 参数确定插入了多少个元素以及将它们初始化为哪些值:
#include <iostream>
#include <list>
#include <vector>

int main()
{
    std::list<int> mylist;
    std::list<int>::iterator it;

    // set some initial values:
    for (int i = 1; i <= 5; ++i) mylist.push_back(i); // 1 2 3 4 5

    it = mylist.begin();
    ++it;       // it points now to number 2           ^

    mylist.insert(it, 10);                        // 1 10 2 3 4 5

    // "it" still points to number 2                      ^
    mylist.insert(it, 2, 20);                      // 1 10 20 20 2 3 4 5

    --it;       // it points now to the second 20            ^

    std::vector<int> myvector(2, 30);
    mylist.insert(it, myvector.begin(), myvector.end());
    // 1 10 20 30 30 20 2 3 4 5
    //               ^
    std::cout << "mylist contains:";
    for (it = mylist.begin(); it != mylist.end(); ++it)
        std::cout << ' ' << *it;
    std::cout << '\n';

    return 0;
}

在这里插入图片描述

8. erase()

在这里插入图片描述

  1. 从列表容器中删除单个元素(位置)或一系列元素([第一个,最后一个))。这有效地减少了容器的大小,减少了被销毁的元素的数量。
  2. 与其他标准序列容器不同,list和forward_list对象经过专门设计,可以有效地在任何位置插入和删除元素,即使是在序列的中间。
#include <iostream>
#include <list>

int main()
{
    std::list<int> mylist;
    std::list<int>::iterator it1, it2;

    // set some values:
    for (int i = 1; i < 10; ++i) mylist.push_back(i * 10);

    // 10 20 30 40 50 60 70 80 90
    it1 = it2 = mylist.begin(); // ^^
    advance(it2, 6);            // ^                 ^
    ++it1;                      //    ^              ^

    it1 = mylist.erase(it1);   // 10 30 40 50 60 70 80 90
    //    ^           ^

    it2 = mylist.erase(it2);   // 10 30 40 50 60 80 90
    //    ^           ^

    ++it1;                      //       ^        ^
    --it2;                      //       ^     ^

    mylist.erase(it1, it2);     // 10 30 60 80 90
    //        ^

    std::cout << "mylist contains:";
    for (it1 = mylist.begin(); it1 != mylist.end(); ++it1)
        std::cout << ' ' << *it1;
    std::cout << '\n';

    return 0;
}

在这里插入图片描述

9. clear()

在这里插入图片描述

从列表容器中删除所有元素(已销毁),并使容器的大小为0。

#include <iostream>
#include <list>

int main()
{
    std::list<int> mylist;
    // set some values:
    for (int i = 1; i < 10; ++i) mylist.push_back(i * 10);
    // 10 20 30 40 50 60 70 80 90
    std::cout << mylist.size() << std::endl;
    mylist.clear();
    std::cout << mylist.size() << std::endl;

    return 0;
}

在这里插入图片描述

10. swap

在这里插入图片描述

  1. 用x的内容交换容器的内容,x是另一个相同类型的列表。尺寸可能不同。
  2. 在调用该成员函数之后,该容器中的元素是调用之前在x中的元素,而x的元素是在此中的元素。所有迭代器、引用和指针对于交换的对象仍然有效。
  3. 请注意,存在一个具有相同名称的非成员函数swap,该函数使用与该成员函数类似的优化重载该算法。
  4. 容器分配器是否也被交换没有定义,除非在这种情况下,适当的分配器特性明确指示它们应该传播。
#include <iostream>
#include <list>

int main()
{
    std::list<int> mylist1,mylist2;
    // set some values:
    for (int i = 1; i < 10; ++i)
    {
        mylist1.push_back(i * 10);// 10 20 30 40 50 60 70 80 90
        mylist2.push_back(i);//1 2 3 4 5 6 7 8 9 
    }
    for (auto e : mylist1)
        std::cout << e << " ";
    std::cout << std::endl;
    for (auto e : mylist2)
        std::cout << e << " ";
    std::cout << std::endl;

    mylist1.swap(mylist2);//交换元素
    for (auto e : mylist1)
        std::cout << e << " ";
    std::cout << std::endl;
    for (auto e : mylist2)
        std::cout << e << " ";
    std::cout << std::endl;
    return 0;
}

在这里插入图片描述

五、迭代器

1. begin()

在这里插入图片描述

返回一个迭代器,该迭代器指向list容器中的第一个元素。
请注意,与返回对第一个元素的引用的member-list::front不同,此函数返回指向它的双向迭代器。
如果容器为空,则不应该解引用返回的迭代器值。

#include <iostream>
#include <list>

int main()
{
    std::list<int> mylist1;
    // 1 2 3  4 5
    for (int i = 1; i <= 5; i++) mylist1.push_back(i);
    std::list<int>::iterator it = mylist1.begin();
    std::cout << *it << std::endl;

    return 0;
}

在这里插入图片描述

2. end()

在这里插入图片描述
返回一个迭代器,该迭代器指向list容器中最后一个元素的下一个元素
因此它不指向任何元素,不能执行解引用操作。
由于标准库的函数所使用的范围不包括其end迭代器所指向的元素,因此此函数经常与list::begin结合使用,以指定一个包括容器中所有元素的范围。
如果容器为空,此函数将返回与list::begin相同的值。

因为end返回的迭代器指向最后一个元素的下一个元素,所以不能解引用,那只需要让这个迭代器先--再解引用,那得到的就是最后一个元素

#include <list>

int main()
{
    std::list<int> mylist1;
    // 1 2 3  4 5
    for (int i = 1; i <= 5; i++) mylist1.push_back(i);
    std::list<int>::iterator it = mylist1.end();
    std::cout << *(--it) << std::endl;

    return 0;
}

在这里插入图片描述

3. rbegin()

在这里插入图片描述
返回一个反向迭代器,指向容器中的最后一个元素(即其反向开头)。
反向迭代器向后迭代:增加它们会将它们移向容器的开头。
rbegin指向成员端将指向的元素之前的元素。
请注意,与返回对同一元素的引用的member-list::back不同,此函数返回一个反向双向迭代器。

#include <iostream>
#include <list>

int main()
{
    std::list<int> mylist1;
    // 1 2 3  4 5
    for (int i = 1; i <= 5; i++) mylist1.push_back(i);
    std::list<int>::reverse_iterator it = mylist1.rbegin();
    std::cout << *(it) << std::endl;

    return 0;
}

在这里插入图片描述

4. rend()

在这里插入图片描述
返回一个反向迭代器,该迭代器指向列表容器中第一个元素之前的理论元素(被视为其反向端)。
list::rbegin和list::rend之间的范围包含容器的所有元素(按相反顺序)。

因为rend返回的迭代器指向第一个元素的前一个元素,所以不能解引用,那只需要让这个迭代器先--再解引用,那得到的就是第一个元素

#include <iostream>
#include <list>

int main()
{
    std::list<int> mylist1;
    // 1 2 3  4 5
    for (int i = 1; i <= 5; i++) mylist1.push_back(i);
    std::list<int>::reverse_iterator it = mylist1.rend();
    std::cout << *(--it) << std::endl;

    return 0;
}

在这里插入图片描述

5. cbegin()、cend()、crbegin()、crend()

在这里插入图片描述
cbegin()、cend()、crbegin()、crend()所代表的含义分别和begin()、end()、rbegin()、rend()一样,唯一的区别是前者是const版本的,只能读,不能修改,而后面的读写都可以。
以begin()和cbegin()为例:
可以看到begin是可以修改的,(it)++之后再输出it就从1变成2了。
在这里插入图片描述
cbegin直接报错了。
在这里插入图片描述

六、 操作函数

1. splice()

在这里插入图片描述
splice函数用于两个list容器之间的拼接,其有三种拼接方式:

  1. 将整个容器拼接到另一个容器的指定迭代器位置。
  2. 将容器当中的某一个数据拼接到另一个容器的指定迭代器位置。
  3. 将容器指定迭代器区间的数据拼接到另一个容器的指定迭代器位置。
#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> lt1(4, 2);
	list<int> lt2(4, 6);
	lt1.splice(lt1.begin(), lt2); 
	//将容器lt2拼接到容器lt1的开头
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl; //6 6 6 6 2 2 2 2 

	list<int> lt3(4, 2);
	list<int> lt4(4, 6);
	lt3.splice(lt3.begin(), lt4, lt4.begin()); 
	//将容器lt4的第一个数据拼接到容器lt3的开头
	for (auto e : lt3)
	{
		cout << e << " ";
	}
	cout << endl; //6 2 2 2 2 

	list<int> lt5(4, 2);
	list<int> lt6(4, 6);
	lt5.splice(lt5.begin(), lt6, lt6.begin(), lt6.end()); 
	//将容器lt6的指定迭代器区间内的数据拼接到容器lt5的开头
	for (auto e : lt5)
	{
		cout << e << " ";
	}
	cout << endl; //6 6 6 6 2 2 2 2
	return 0;
}

在这里插入图片描述

2. unique()

在这里插入图片描述
不带参数的版本(1)从容器中每个连续的相等元素组中删除除第一个元素外的所有元素。
请注意,只有当一个元素与紧挨在它前面的元素进行比较时,它才会从列表容器中删除。因此,此函数对排序列表特别有用。

#include <iostream>
#include <list>

int main()
{
    std::list<int> mylist1;
    mylist1.push_back(4);
    for (int i = 1; i <= 5; i++) mylist1.push_back(5);
    mylist1.push_back(4);
    for (int i = 1; i <= 5; i++) mylist1.push_back(5);
    mylist1.unique();
    for (auto e : mylist1)
        std::cout << e << " ";

    return 0;
}

在这里插入图片描述

3. remove()

在这里插入图片描述
从容器中删除所有与值相等的元素。这将调用这些对象的析构函数,并按删除的元素数量减少容器大小。
与成员函数list::erase(使用迭代器)按位置擦除元素不同,此函数(list::remove)按元素的值删除元素。

#include <iostream>
#include <list>

int main() {
    std::list<int> myList1 = { 1,2,3,4,5,3 };
    myList1.remove(3);
    for (int num : myList1) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这里插入图片描述

4. remove_if()

在这里插入图片描述
这个成员函数用于根据给定的谓词函数 pred 移除满足特定条件的元素。
谓词函数 pred 接受一个参数(参数类型同*迭代器返回的类型相同)并返回一个布尔值,用于判断是否需要移除该元素。如果谓词返回 true,则该元素将被移除。
从容器中删除Predicate pred返回true的所有元素。这将调用这些对象的析构函数,并通过移除的元素数量来减少容器大小。
函数为每个元素调用pred(*i)(其中i是该元素的迭代器)。列表中返回true的任何元素都将从容器中删除。

#include <iostream>
#include <list>

bool judge(int num)
{
    return num % 2 == 0;
}
int main() {
    std::list<int> myList1 = { 1,2,3,4,5,6,7,8,9 };
    myList1.remove_if(judge);
    for (int num : myList1) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这里插入图片描述

5. merge()

在这里插入图片描述
C++标准库中的std::list的merge函数要求两个列表在合并前都已经是排序的。如果两个列表不是有序的,那么merge函数的结果将是不确定的,因为它会按照元素的大小顺序来合并两个列表。

  1. 第一个成员函数用于将另一个列表 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;
}

在这里插入图片描述
2. 第二个成员函数不仅可以将两个list合并,还能指明排序方法。上面是升序,我们可以弄成降序

在这里插入图片描述

6. sort()

在这里插入图片描述
对列表中的元素进行排序,改变它们在容器中的位置。

  1. 默认升序
int main() {
    std::list<int> myList1 = { 3,1,5,4,2,3 };
    myList1.sort();
    for (int num : myList1) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这里插入图片描述

  1. 可以指定如何排序,比如这里我们让它降序
    在这里插入图片描述

7 . reserve()

在这里插入图片描述
颠倒列表容器中元素的顺序

#include <iostream>
#include <list>

int main() {
    std::list<int> myList1 = { 1,2,3,4,5 };
    myList1.reverse();
    for (int num : myList1) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

在这里插入图片描述

七、 sort排序效率测评

list容器虽然有一个排序的sort函数,但我们一般是不会使用的,因为效率太低了。
现在我们使用下面的代码来测试vector和list排序的性能,看一下结果。
现在产生1000000个随机数,分别放到vector和list中,然后list调自己提供的sort,vector调库里面的sort,我们来对比一下它们的运行时间:

多运行几次,可以发现虽然每次时间都不太一样,但是vector的排序总是比list快很多。

void test_op1()
{
	srand(time(0));
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);
	list<int> lt1;

	for (int i = 0; i < N; ++i)
	{
		auto e = rand();
		v.push_back(e);
		lt1.push_back(e);
	}

	int begin1 = clock();
	sort(v.begin(), v.end());// vector排序
	int end1 = clock();

	int begin2 = clock();
	lt1.sort();//list排序
	int end2 = clock();

	printf("vector sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

在这里插入图片描述

既然如此,那我们就比点有可比性的。我们先分别向两个list中都插入1000000个随机数。然后一个是先把list的数据拷贝到vector中,利用vector排序完之后再拷贝回list中,另一个是直接调用list的成员函数sort进行排序,比比谁快。
可以看到还是前者快,所以一般不用list的sort因为效率太低了。

void test_op1()
{
	srand(time(0));
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);
	list<int> lt1;
	list<int> lt2;

	for (int i = 0; i < N; ++i)
	{
		auto e = rand();
		lt2.push_back(e);
		lt1.push_back(e);
	}
	// 拷贝到vector排序,排完以后再拷贝回来
	int begin1 = clock();
	// 先拷贝到vector
	for (auto e : lt1)
	{
		v.push_back(e);
	}
	// 排序
	sort(v.begin(), v.end());
	// 拷贝回去
	size_t i = 0;
	for (auto& e : lt1)
	{
		e = v[i++];
	}

	int end1 = clock();

	int begin2 = clock();
	lt2.sort();
	int end2 = clock();

	printf("vector sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

在这里插入图片描述

八、 list迭代器失效问题

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

当使用 std::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;
}

在这里插入图片描述
但如果此时再次*it就会出问题,这就是迭代器失效导致的。
在这里插入图片描述

在上面的示例中,当我们在第二个元素位置处使用 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;
}

在这里插入图片描述

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

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

相关文章

设计模式之装饰者模式DecoratorPattern(四)

一、模板模式介绍 模板方法模式&#xff08;Template Method Pattern&#xff09;&#xff0c;又叫模板模式&#xff08;Template Pattern&#xff09;&#xff0c; 在一个抽象类公开定义了执行它的方法的模板。它的子类可以更需要重写方法实现&#xff0c;但可以成为典型类中…

d16(149-153)-勇敢开始Java,咖啡拯救人生

跳过了p151 四小时的讲题我不敢听&#xff1a;) Stream Stream流&#xff0c;是JDK8后新增的API&#xff0c;可以用于操作集合或者数组的数据 优势&#xff1a;大量结合了Lambda的语法风格&#xff0c;该方式更强大更简单&#xff0c;代码简洁&#xff0c;可读性好 常用方法 …

Mycat(三)读写分离双主双从

文章目录 搭建双主双从双主机配置双从机配置双从配置两个主机互相复制停止从服务复制功能重新配置主从 修改 Mycat 的集群配置实现多种主从双主双从集群角色划分增加两个数据源修改集群配置文件读写分离配置扩展&#xff08;1&#xff09;读写分离(一主一从,无备)(m是主,s是从)…

Grafana页面嵌入自建Web应用页面

目录 一、应用场景 二、实现方式 1、修改Grafana配置文件 2、获取监控页面url 3、隐藏左侧和顶部菜单 一、应用场景 需要将Grafana监控页面嵌入自建Web应用页面&#xff0c;使Grafana监控页面成为自建Web应用的一部分。 二、实现方式 总体思路&#xff1a;修改Grafana配…

刷机维修进阶教程-----红米note7 修复基带 更改参数 nv损坏故障 实例步骤操作解析

在前面的博文中我有说过。不管刷更改参数还是修复基带,尽可能的情况下备份一些主要分区,上期讲了小米6x 小米5 小米6这些机型更改参数的具体步骤。今天的教程以红米note7为例解析下改参数和修复nv损坏的具体步骤,两者操作实际没有什么冲突。有兴趣的友友建议多看下我关于…

10G MAC层设计系列-(2)MAC RX模块

一、概述 MAC RX模块的需要进行解码、对齐、CRC校验。 因为在空闲的时候10G PCS/PMA会一直向外吐空闲符&#xff08;x07&#xff09;所以需要根据开始符、结束符将有效数据从码流中截取&#xff0c;也就是解码。 因为开始字符的所在位置有两种形式&#xff0c;而结束字符的位…

大数据学习笔记14-Hive基础2

一、数据字段类型 数据类型 &#xff1a;LanguageManual Types - Apache Hive - Apache Software Foundation 基本数据类型 数值相关类型 整数 tinyint smallint int bigint 小数 float double decimal 精度最高 日期类型 date 日期 timestamps 日期时间 字符串类型 s…

UE C++ 链表

目录 概要单链表双向链表头插入尾插入中间插入删除查找 小结 概要 链表 简单说明&#xff0c;链表有单链表&#xff0c;双向链表&#xff0c;循环链表(本篇文章以UE c代码说明)。链表的操作&#xff0c;插入&#xff0c;删除&#xff0c;查找。插入&#xff0c;删除效率高&…

练习题(2024/4/29)

在深度优先遍历中&#xff1a;有三个顺序&#xff0c;前中后序遍历 这里前中后&#xff0c;其实指的就是中间节点的遍历顺序&#xff0c;只要记住 前中后序指的就是中间节点的位置就可以了。 如图 1二叉树的前序遍历 给你二叉树的根节点 root &#xff0c;返回它节点值的 前…

【项目】仿muduo库One Thread One Loop式主从Reactor模型实现高并发服务器(Http测试板块)

【项目】仿muduo库One Thread One Loop式主从Reactor模型实现高并发服务器&#xff08;Http测试板块&#xff09; 一、使用Http网页界面1、main.cc原码和index.html原码2、运行结果&#xff08;1&#xff09;测试结果1&#xff1a;用index.html内部的代码&#xff08;2&#xf…

《HelloGitHub》第 97 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 Python、…

win下vscode的vim切换模式的中英文切换

问题描述 在vscode中安装vim插件后&#xff0c;如果insert模式下完成输入后&#xff0c;在中文输入方式下按esc会发生无效输入&#xff0c;需要手动切换到英文。 解决方法 下载完成vscode并在其中配置vim插件下载github—im-select.exe插件&#xff08;注意很多博文中的gitcod…

Microsoft Threat Modeling Tool 使用(二)

主界面 翻译 详细描述 选择了 “SDL TM Knowledge Base (Core)” 模板并打开了一个新的威胁模型。这个界面主要用于绘制数据流图&#xff08;Data Flow Diagram, DFD&#xff09;&#xff0c;它帮助您可视化系统的组成部分和它们之间的交互。以下是界面中各个部分的功能介绍&a…

软件设计师-重点的行为型设计模式

一、命令模式&#xff08;Command&#xff09;&#xff1a; 意图&#xff1a;&#xff08;上午题&#xff09; 将一个请求封装为一个对象&#xff0c;从而使得可以用不同的请求对客户进行参数化&#xff1b;对请求排队或记录请求日志&#xff0c;以及支持可撤销的操作。 结构…

Django-基础篇

Django是一个开放源代码的Web应用框架&#xff0c;由Python语言编写。它遵循MVC&#xff08;Model-View-Controller&#xff09;的软件设计模式&#xff0c;使开发者能够以高效、可扩展和安全的方式构建Web应用程序。 Django具有以下特点和优势&#xff1a; 强大的功能&#x…

[技术小技巧] 可视化分析:在jupyter中使用d3可视化树形结构

首先在python中定义一个字符串&#xff0c;记录d3.js绘制属性图的js脚本代码模版。其中{{data}}就是将来要被替换的内容。 d3_code_template """ // 创建树状结构数据 var treeData {{data}};// 创建d3树布局 var margin { top: 20, right: 90, bottom: 30,…

行业推荐:数据防泄漏软件首先解决方案

随着信息时代的快速发展&#xff0c;数据安全已成为企业经营的关键之一。然而&#xff0c;数据泄漏事件时有发生&#xff0c;不仅可能导致巨大的经济损失&#xff0c;更会损害企业的声誉和客户信任。 为了帮助企业有效地保护数据安全&#xff0c;Ping32 数据防泄漏系统应运而生…

【跟我学RISC-V】认识RISC-V指令集并搭建实验环境

写在前面 现在计算机的体系架构正是发展得如火如荼的时候&#xff0c;占领桌面端市场的x86架构、占领移动端市场的arm架构、在服务器市场仍有一定地位的mips架构、国产自研的指令集loongarch架构、还有我现在要讲到的新型开源开放的RISC-V指令集架构。 我先说一说我的学习经历…

Facebook的语言学:社交媒体如何影响我们的沟通方式

1. 引言 社交媒体已经成为人们日常生活中不可或缺的一部分&#xff0c;而Facebook作为其中最具影响力的平台之一&#xff0c;不仅改变了人们之间的社交方式&#xff0c;也对我们的语言学产生了深远的影响。本文将深入探讨Facebook的语言学特点&#xff0c;以及它如何塑造和改变…

【C++题解】1608. 三位数运算

问题&#xff1a;1608. 三位数运算 类型&#xff1a;基本运算、拆位求解 题目描述&#xff1a; 小丽在编程课上学会了拆位运算&#xff0c;她已经可以拆出一个三位整数的百位、十位和个位了&#xff0c;她想知道这个整数的&#xff08;百位 十位&#xff09; / &#xff08;…
最新文章