stl 迭代器

参考:

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 编译并运行,得到:

posted @ 2022-02-23 11:49  小夕nike  阅读(76)  评论(0编辑  收藏  举报