【C++笔记】List流食般投喂

本编介绍STL容器之List~
++1、迭代器的功能&性质++

++2、辨别模板中应使用哪种迭代器++

++3、list接口的介绍和使用++

++4、模板的按需实例化++

++5、List中迭代器失效的问题++

++6、C++11统一初始化++

++7、默认构造、拷贝构造、赋值运算符重载、析构函数的简便实现写法++
1、迭代器的功能&性质

区分迭代器,可从功能与性质两方面区分。

📌功能:

|------------------------|
| iterator |
| reverse_iterator |
| const_iterator |
| const_reverse_iterator |

📌性质:

|----|-------------------------------|--------------|
| 单向 | forward_list/unordered_map... | 支持:++ |
| 双向 | list/map/set... | 支持:++/-- |
| 随机 | vector/string/deque... | 支持:++/--/+/- |

💡迭代器性质的不同,其支持的运算就不同,这种差异源自于底层结构的区别。

💡对于这三个性质,可以将其归类:

|-----------------------------|
| 随机迭代器可以看作是特殊的双向迭代器,特殊的单向迭代器 |
| 双向迭代器可以看作是特殊的单向迭代器 |
| 他们之间可以特殊化的归为其一类~ |

2、辨别模板中应使用哪种迭代器

|----------|---------------|
| 随机迭代器 | Random Access |
| 双向迭代器 | Bidirectional |
| 单向迭代器 | Forward |
| 包含这三个的~ | Input/Output |

📌在模板中:

看到 "Random Access",说明:可用迭代器范围只能用随机迭代器;

看到 "Bidirectional",说明:可用迭代器范围能用 随机/双向 迭代器;

看到 "Forward",说明:可用迭代器范围能用 随机/双向/单向 迭代器;

看到 "Input/Output",说明:可用迭代器范围能用 随机/双向/单向 迭代器。
3、list接口的介绍和使用

📌此处只介绍与vector有"差异"的接口~

| emplace_back | 对于单参数的尾插,push_back和emplace_back是一样的,都要构造再拷贝;但是emplace_back是支持多参数的,对于多参数为一个元素的尾插,emplace_back就直接构造了。 |
| resize | 在链表中的 resize 就没有扩容的概念了。 |
| splice | 有两个list,将一个list里面的一个数或一段数据剪切掉,再粘贴到另一个list上。 |
| unique | 去重,但是是针对于有序的数据。 |
| merge | 合并两个链表,被拿去合并的链表合并后变成空链表,合并成的链表保持升序。 |

sort 对于小数据,list自带的 sort 快;对于大数据,algorithm里面的 sort 快。

4、模板的按需实例化

模拟实现List中的具体例子:

cpp 复制代码
// 模板 可以实例化出下面两个 -- 正常版/const版
template <class T, class Ref, class Ptr>
struct list_iterator
{
	typedef list_node<T> Node;
	typedef list_iterator<T, Ref, Ptr> Self;

	Node* _node;

	list_iterator(Node* node)
		:_node(node)
	{ }

	Ref operator* ()
	{
		return _node->_data;
	}

	Ptr operator-> () // 强制语法规定:返回指针
	{
		return &_node->_data;
	}

	Self& operator++ ()
	{
		_node = _node->_next;
		return *this;
	}

	Self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}

	bool operator!= (const Self& s) const
	{
		return _node != s._node;
	}

	bool operator== (const Self& s) const
	{
		return _node == s._node;
	}
};

📌该模板在没有具体需求时,编译器不会细致扫描,就像你知道你记得你有某样东西一样。当有具体需求时,模板根据具体需求产生对应的实例化类,供具体需求使用。
5、List中迭代器失效的问题

📌List在物理空间上是不连续的,而且迭代器是经过努力转化出来的,指向哪里就指向哪里,所以插入时,迭代器不失效。

cpp 复制代码
测试代码
void test_list2()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);

	list<int>::iterator it = lt.begin();
	
	// 插入迭代器不会失效
	lt.insert(it, 10);
	*it += 100;

	list<int>::iterator it1 = lt.begin();
	while (it1 != lt.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
}

📌List在删除结点时,当前迭代器就像是野指针一样,所以List删除时,迭代器是失效的。

cpp 复制代码
测试代码
void test_list2()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);	
    
    // 删除所有偶数
	// 迭代器失效
	list<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		if (*it % 2 == 0)
		{
			lt.erase(it);
		}
		else
		{
			++it;
		}
	}
	list<int>::iterator it1 = lt.begin();
	while (it1 != lt.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
}

6、C++11统一初始化

C++11之后支持了这样的初始化的方式:

cpp 复制代码
// 直接构造
list<int> lt0({ 1,2,3,4,5,6 });
// 隐式类型转换
list<int> lt1 = { 1,2,3,4,5,6,7,8 };
const list<int>& lt3 = { 1,2,3,4,5,6,7,8 };

func(lt0);
func({ 1,2,3,4,5,6 });

其中隐式类型转换:大括号一串的数字会转换成 "initializer_list<int>" 类型,"initializer_list":初始化列表。

cpp 复制代码
auto il = { 10, 20, 30 };
initializer_list<int> il = { 10, 20, 30 };
cout << typeid(il).name() << endl;
cout << sizeof(il) << endl;

实现方式是前后两个指针,仅作了解。
7、默认构造、拷贝构造、赋值运算符重载、析构函数的简便实现写法

cpp 复制代码
void empty_init()
{
	_head = new Node;
	
	_head->_next = _head;
	_head->_prev = _head;
	_size = 0;
}

// 默认构造
list()
{
	empty_init();
}

// l2(l1) 拷贝构造
list(const list<T>& lt)
{
	empty_init();

	for (auto& e : lt)
	{
		push_back(e);
	}
}

// C++11统一初始化的隐式类型转换
list(initializer_list<T>& lt)
{
	empty_init();

	for (auto& e : lt)
	{
		push_back(e);
	}
}

// l2 = l1 赋值运算符重载
list<int>& operator= (list<T> lt)
{
	swap(lt);
}

void swap(list<T>& lt)
{
	std::swap(_head, lt._head);
	std::swap(_size, lt._size);
}

// 析构
~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

void clear()
{
	auto it = begin();
	while (it != end())
	{
		it = earse(it);
	}
}
相关推荐
信仰8745 小时前
HCIA-华为数通基础理论与实践08
网络·笔记·华为
Lucis__8 小时前
基于Cache替换算法的LRU缓存实现
数据结构·c++·算法·缓存·lru
Cx330❀14 小时前
【MySQL基础】一文吃透“表的约束”:从 Null/Default 到主外键的终极安全法则
linux·服务器·数据库·c++·mysql·安全
c2385614 小时前
第二篇:《测试指挥官:可视化单题自测框架(含 assert 实操)》
java·数据库·c++·算法·安全性测试
辞旧 lekkk14 小时前
【Redis初阶】常见数据类型
开发语言·数据库·c++·redis·学习·缓存·bootstrap
帅次15 小时前
Kotlin 与 Java 互操作:混合工程里的平台类型与 API 边界
java·开发语言·kotlin·suspend·nullable
dtq042417 小时前
C语言-结构体详解
c语言·开发语言·学习
梅雅达编程笔记17 小时前
编程启蒙|Scratch 转 Python 系列第9天:字典/哈希表积木双向对照(AI大模型参数配置表实战)
开发语言·人工智能·python·numpy·pandas
持力行17 小时前
C++与Java变量声明、定义及内存分配的核心区别
java·开发语言·c++