stl 迭代器
参考:
- stl 源码剖析
- https://www.cnblogs.com/WindSun/p/11450701.html
- https://www.cplusplus.com/doc/oldtutorial/templates/
1. 概述
本篇文章主要记录学习 stl 源码剖析 后自己对迭代器的理解。
2. stl 迭代器
stl 主要分为 3 类,分别是:
- 容器。用于存储数据
- 算法。用于对容器内的数据施加某种算法
- 迭代器。用于遍历容器内的元素,并用作算法的输入结构
迭代器的行为有点像智能指针,提供了基础的解引用(*)和指针访问(->)功能。
迭代器的实现依赖于容器的实现,所以每一种容器都对应自己的迭代器。但是需要主要,并不是每种容器都有迭代器,比如栈、队列、优先级队列就没有自己的迭代器,不提供遍历容器元素的功能。
stl 迭代器按其访问元素属性和遍历能力分为 5 种类别:
- input iterator,只读迭代器,这种迭代器在表达式中不能被写入任何数据
- ouput iterator,只写迭代器,这种迭代器在表达式中不能被读取数值
- forward iterator,前向迭代器,提供 iter++ 这种访问,且此迭代器可读可写
- bidirectional iterator,双向迭代器,提供 iter++ 和 iter-- 这种访问,且此迭代器可读可写
- random access iterator,随机访问迭代器,提供 iter++、iter-- 和 iter[n] 这种访问,且此迭代器可读可写
3. 特性萃取
trait(特性/类型/型别)指迭代器内部元素的类型,比如我有一个 intIter(迭代器内部元素是 int),那么此迭代器的 trait 就是 int;我有一个 MyClassIter(迭代器内部元素是 MyClass 对象),那么此迭代器的 trait 就是 MyClass。
了解迭代器所指元素类型有什么作用呢?主要是为了算法泛型编程服务,下面开始分析。
假设我有一个算法,接收一个迭代器:
// 模板 T 表示接收任何迭代器
template <class T>
return_type my_algo(T a) {
// 取出迭代器所指元素的值,value_type 是迭代器所指元素的类型,这里还不知道
value_type tmp = *a;
// 简单的进行乘加运算,生成一个 value_type 类型的中间变量
value_type result = tmp*10 + 10;
// 返回中间变量,这里也不知道元素的类型
return result;
}
上面代码中存在的一个编译问题就是 value_type 不知道是什么类型,那么要是将 value_type 定义在迭代器内部,能否解决编译问题呢?如下:
template <class T>
typename T::value_type my_algo(T a) {
typename T::value_type tmp = *a;
typename T::value_type result = tmp*10 + 10;
return result;
}
上面代码似乎不错,对于 intIter、MyClassIter 都适用,但是还是存在一个问题,如果是按如下调用:
int* tmp = new int(100);
// 期望调用行为
int result = my_algo(tmp);
这种调用将会编译报错,因为没有 int::value_type 的定义。
对于函数输入参数和内部表达式,我们还可以为 int* 写一个特化的 my_alog(T* a) 版本,但是对于返回值依然没有办法,模板推导无法推导返回值类型。我们需要一种新的方法:
// 定义一个特性萃取类,用于匹配普通迭代器
template <class T>
class IterTraits {
public:
typedef typename T::value_type value_type;
};
// 定义一个特性萃取类,用于匹配内置指针类型
template <class T>
class IterTraits<T*> {
public:
typedef T value_type;
};
template <class T>
typename IterTraits<T>::value_type my_algo(T a) {
typename IterTraits<T>::value_type tmp = *a;
typename IterTraits<T>::value_type result = tmp*10 + 10;
return result;
}
在上面的代码中,IterTraits 类称为特性萃取类,用于萃取迭代器或内置指针所指元素的类型,这样编译才能通过。
下面是一个完整示例(注意,为简单起见,下面的迭代器只是包含一个元素的简单示例):
#include <stdio.h>
class MyClass {
public:
MyClass(int v): value(v) {}
MyClass& operator*(int mul) { value *= mul; return *this; }
MyClass& operator+(int add) { value += add; return *this; }
MyClass& operator-(int sub) { value -= sub; return *this; }
int get_value() { return value; }
private:
int value;
};
// 装载 MyClass 元素类型的迭代器
class IterMyClass {
public:
typedef MyClass value_type;
typedef MyClass& ref_type;
IterMyClass(MyClass* a): value(a) {}
MyClass& operator*() { return *value; }
private:
MyClass* value;
};
// 装载 int 元素类型的迭代器
class IterInt {
public:
typedef int value_type;
typedef int& ref_type;
IterInt(int* a): value(a) {}
int& operator*() { return *value; }
private:
int* value;
};
// 装载 float 元素类型的迭代器
class IterFloat {
public:
typedef float value_type;
typedef float& ref_type;
IterFloat(float* a): value(a) {}
float& operator*() { return *value; }
private:
float* value;
};
// 特性萃取类,用于萃取迭代器所指元素类别
class IterTraits {
public:
typedef typename T::value_type value_type;
typedef typename T::ref_type ref_type;
};
// 特化版本,用于萃取内置指针所指元素类别
template <class T>
class IterTraits<T*> {
public:
typedef T value_type;
typedef T& ref_type;
};
// 特化版本,用于萃取内置 const 指针所指元素类别,元素类别消除 const 属性
template <class T>
class IterTraits<const T*> {
public:
typedef T value_type;
typedef const T& ref_type;
};
template <class T>
typename IterTraits<T>::value_type my_algo(T a) {
// 迭代器解引用后使用引用类型来接收
typename IterTraits<T>::ref_type tmp = *a;
// 临时变量直接使用非 const、非引用类型
typename IterTraits<T>::value_type result = tmp*10 + 10;
// 返回临时变量,可以触发返回值优化
return result;
}
int main() {
int res00 = my_algo(new int(100));
printf("result: %d\n", res00);
float res01 = my_algo(new float(100.01));
printf("result: %f\n", res01);
const float* const_value = new float(100.02);
float res03 = my_algo(const_value);
printf("result: %f\n", res03);
IterInt iter_int(new int(101));
int res04 = my_algo(iter_int);
printf("result: %d\n", res04);
IterFloat iter_float(new float(100.03));
float res05 = my_algo(iter_float);
printf("result: %f\n", res05);
IterMyClass class_value(new MyClass(102));
MyClass res06 = my_algo(class_value);
printf("result: %d\n", res06.get_value());
return 0;
}
使用 g++ mytest.cpp -o mytest --std=c++11 编译并运行,得到: