C++ list介绍

文章目录

  • [1. list简介](#1. list简介)
  • [2. list的实现框架](#2. list的实现框架)
    • [2.1 链表结点](#2.1 链表结点)
    • [2.2 链表迭代器](#2.2 链表迭代器)
    • [2.3 链表](#2.3 链表)
  • [3. list迭代器及反向迭代器设计](#3. list迭代器及反向迭代器设计)
    • [3.1 list迭代器](#3.1 list迭代器)
    • [3.2 list反向迭代器](#3.2 list反向迭代器)
    • [3.3 list迭代器失效](#3.3 list迭代器失效)
  • [4. list与vector比较](#4. list与vector比较)

1. list简介

list,即链表。

链表的种类有很多,是否带头结点,是否循环,是否双向------list是带头双向循环链表

关于list的使用本文中不作介绍,本文将重点放在list的实现框架、list的迭代器和反向迭代器设计、list的迭代器失效上。

2. list的实现框架

list的实现,实质上实现了三个类。

对于链表而言,只需要一个指向头结点的指针,即可拿到整个链表。这句话中就包括了要实现的三个类:链表、链表的迭代器和链表的结点。

2.1 链表结点

首先,我们需要将链表的结点封装成一个类。C++中很少使用内部类,一般定义成struct(即class + public), 允许外部使用即可。

结点类是最基础的一个类,指向它实例化出对象的指针,既是链表迭代器类的成员变量,也是链表类的成员变量。

2.2 链表迭代器

在vector当中,迭代器是用原生指针即可实现,这是因为vector的底层实际上是一个具有连续存储空间的动态数组,迭代器的行为与原生指针的行为相同。

但对于链表,链表结点的存储空间不一定是连续的,因此原生指针的行为不一定能达到目的。而将链表的迭代器封装为一个类,即可通过运算符重载,实现想要的行为。

2.3 链表

链表类,是我们最终的主体。它仍然以指向结点的指针作为成员变量,但是这个成员变量是特殊的,其为指向链表头结点(即哨兵位)的指针。

3. list迭代器及反向迭代器设计

3.1 list迭代器

要设计list的迭代器,就会想到还要设计list的const迭代器。

需要说明的是,const迭代器并不是简单地在普通迭代器前加上const。

复制代码
const iterator it;
//这里const实际修饰it,使得it本身不能改变
//const迭代器并不是迭代器本身不能改变,而是迭代器指向地对象不能被修改

因此,通过以上分析可得,如果要将迭代器封装成类,普通迭代器与const迭代器,实为两个不同的类。

但如果两种迭代器都分别实现的话,会发现代码只有很细微的差别,此时我们会想到将迭代器设计为类模板普通迭代器与const迭代器都是这个模板的实例化。

那么,如何设计这个类模板呢?

考虑到,普通迭代器与const迭代器,只有在解引用运算符重载与成员访问运算符(即->)重载时有返回值上的区别(普通指针与const指针、普通引用与const引用),所以需要三个模板参数------确定节点数据类型的类型参数T,确定指针类型的参数Ptr和确定引用类型的参数Ref。

有几点需要说明:

  1. list的迭代器是bidirectional iterator,即双向迭代器。双向迭代器只重载++和- -,不重载加减一个具体数字。双向迭代器,关系运算符也只重载 == 和 ! = 。
  2. 关于解引用操作符与成员访问操作符的重载说明如下。

链表的结点是一个类,对迭代器解引用是为了拿到其中的数据
当迭代器是用 -> 这个操作符时,说明结点中所存储的数据应是一个自定义类型,通过->操作符去访问这个自定义类型允许外部访问的成员函数或成员变量

复制代码
//使用代码示例如下所示
int main()
{
	list<string> l1;
	l1.push_back("abc");
	l1.push_back("efg");
	list<string>::iterator it = l1.begin();
	while(it != l1.end())
	{
		//以下两条语句打印出的结果相同
		cout << (*it).size() << endl;
		cout << it->size() << endl;//it->size()实则为it->->size(),有两个箭头,因为->运算符重载中返回的是&_node->_data,只不过通常只写一个箭头
		it++;
	}
	return 0;
}

3.2 list反向迭代器

反向迭代器是一种适配器,或者说是运用了适配器的设计模式实现的,本质上就是对普通迭代器的复用。具体实现时,将普通迭代器设置为反向迭代器的成员变量,实现反向迭代器的成员函数时,复用普通迭代器的成员函数即可。

3.3 list迭代器失效

相比于vector的迭代器失效,list迭代器失效的情况就要少得多。本质上,这是因为链表结构各节点间的插入删除并不会互相影响,不会出现vector中需要前移或后移的情况,而且链表也不需要像vector那样异地扩容。

所以,insert不会导致list迭代器失效,erase只会导致指向被删除结点的迭代器失效。

4. list与vector比较

在属性上,list与vector的差别,可以近似为链表与顺序表的差别,而且list的迭代器为双向迭代器,vector的迭代器是随机迭代器

这个迭代器上的区别,使得vector对象可以使用算法库中的sort(这个函数要求传随机迭代器),而list无法使用,因此list自身实现了一个sort作为成员函数,但是这个list的排序速度是很慢的,甚至慢于将list的数据拷贝到vector中,在vector中排序完后再拷贝会list的速度,因此慎用list的排序。

在功能上,如果想要在任意位置插入和删除数据,推荐使用list;如果想要在任意位置快速存取数据,推荐使用vector。

相关推荐
我命由我1234522 分钟前
VSCode - VSCode 放大与缩小代码
前端·ide·windows·vscode·前端框架·编辑器·软件工具
PT_silver43 分钟前
tryhackme——Abusing Windows Internals(进程注入)
windows·microsoft
愚润求学1 小时前
【C++】类型转换
开发语言·c++
@我漫长的孤独流浪1 小时前
数据结构测试模拟题(4)
数据结构·c++·算法
csdnzzt1 小时前
从内存角度透视现代C++关键特性
c++
爱炸薯条的小朋友2 小时前
C#由于获取WPF窗口名称造成的异常报错问题
windows·c#·wpf
jie188945758662 小时前
C++ 中的 const 知识点详解,c++和c语言区别
java·c语言·c++
明月*清风2 小时前
c++ —— 内存管理
开发语言·c++
Lw老王要学习3 小时前
VScode 使用 git 提交数据到指定库的完整指南
windows·git·vscode
西北大程序猿3 小时前
单例模式与锁(死锁)
linux·开发语言·c++·单例模式