迭代器
昨天忽然想到,emplace_back和push_back,emplace_back本质就是移动语义吗,然后在容器后边直接构造,只有一次构造,我就想,那push_back一个右值的时候,是不是和emplace_back一模一样的啊,然后翻了翻以前自己的博客(请神上身了属于是),然后直接绷不住了,写的什么玩意,一点不深刻。有一种看自己五年前QQ动态的美(人果然不能共情曾经的自己)。
实际上哈,push_back左值右值是不一样的,会调用重载的不同版本的push_back函数,都要调用构造函数创建对象,区别只在于传入左值调用拷贝构造函数,传入右值调用移动构造函数,而emplace_back是完美转发参数给内部,调用的构造函数。
到此为止。
———————————————————不知道华不华丽的分割线—————————————————
迭代器,用b站宇文新粥的说法,就是给指针穿上了华丽的衣服,是指针的钢铁侠战甲
迭代器,实际上是类or结构体,里面封装了一个对应容器的指针,以及所有相关的操作符重载,目的就是让迭代器有指针的所有功能
为什么要迭代器而不是指针
因为容器是多种多样的,比如vector是内存分布在相邻地址,而list就是链表,每个节点相隔甚远,map什么的用红黑树实现就更别说了,而容器算法很多又是通用的,比如sort(list不能用),find等,为了让list也能用类似原生指针的东西访问,我们采用了迭代器。
不同容器的迭代器是不一样的,比如vector就很类似原生指针,而其他的只是重载函数实现了类似效果
容器与迭代器的关系
- 每个容器类(如
std::vector
,std::list
,std::map
等)都在其内部定义了至少一种或多种迭代器类型(例如iterator
和const_iterator
)。这些迭代器类型是该容器的友元类,或者直接定义在容器类内部,以便访问容器的私有成员(如vector
的底层数组指针,list
的节点指针)。 - 容器的
begin()
和end()
成员函数负责创建并返回该容器特定类型的迭代器对象。
迭代器特性 (Iterator Traits)
标准库算法(如 std::sort
, std::find
)是泛型的,它们通过模板参数接收迭代器。为了知道传入的迭代器具备哪些能力(属于哪个类别),以及它指向的元素类型是什么,标准库使用了一个叫做 std::iterator_traits
的模板元编程技术。
std::iterator_traits<Iterator>
可以提取出迭代器的以下信息:
iterator_category
: 迭代器类别(如std::random_access_iterator_tag
)value_type
: 迭代器指向的元素类型difference_type
: 两个迭代器之间的距离类型pointer
: 指向元素类型的指针类型reference
: 元素类型的引用类型
自定义的迭代器需要通过继承 std::iterator
或在迭代器类中定义这些 typedef
来配合 std::iterator_traits
。
type traits(类型萃取)
主要体现在算法如何根据迭代器的特性来选择最优化的实现路径。
核心机制是通过 std::iterator_traits
这个类型萃取模板来实现的。
1. std::iterator_traits
的作用
std::iterator_traits
是一个特殊的类型萃取模板,它的作用是从任何迭代器类型 Iterator
中提取出关于该迭代器的标准信息。这些信息通常是通过迭代器类内部定义的 typedef
来提供的。std::iterator_traits
会查找并暴露这些嵌套类型,使得算法可以通过统一的方式访问它们。
std::iterator_traits<Iterator>
通常会提供以下几个重要的嵌套类型(即萃取出的特性):
value_type
: 迭代器所指向元素的类型。difference_type
: 用于表示两个迭代器之间距离的类型(通常是ptrdiff_t
)。pointer
: 迭代器指向元素的指针类型。reference
: 迭代器指向元素的引用类型。iterator_category
: 这是一个非常重要的特性,它指示了迭代器的能力(例如,输入迭代器、前向迭代器、随机访问迭代器等)。
std::iterator_traits
还为原始指针类型(如 T*
)提供了偏特化,使得原始指针也能像 STL 迭代器一样工作,并被识别为随机访问迭代器。
2. 迭代器分类与标签 (Iterator Categories and Tags)
iterator_category
是连接 Type Traits 和迭代器能力的关键。C++ 定义了一系列的标签类型来表示不同的迭代器类别,它们之间存在继承关系:
std::input_iterator_tag
(输入迭代器)std::output_iterator_tag
(输出迭代器)std::forward_iterator_tag
(前向迭代器) - 继承自input_iterator_tag
和output_iterator_tag
std::bidirectional_iterator_tag
(双向迭代器) - 继承自forward_iterator_tag
std::random_access_iterator_tag
(随机访问迭代器) - 继承自bidirectional_iterator_tag
一个迭代器类型通过在其内部定义 iterator_category
为这些标签类型之一,来声明自己的能力。例如,std::vector
的迭代器内部会定义 typedef std::random_access_iterator_tag iterator_category;
。
3. 算法如何利用 std::iterator_traits
和分类标签
STL 算法(如 std::advance
, std::distance
, std::copy
, std::sort
等)是模板函数,它们接受迭代器作为参数。在算法的内部实现中,它们会使用 std::iterator_traits
来获取迭代器的特性,尤其是 iterator_category
。
基于 iterator_category
的不同,算法会采用不同的实现策略。这通常通过标签分派 (Tag Dispatching) 或 C++17 后的 if constexpr
来实现,这是一种编译时多态。