STL的算法需要知道iterator的相关信息(iterator种类、iterator指向的元素的类型...),来决定使用最优化的算法。比如vector的iterator,那么就可以使用+,-操作;如果是list的iterator,那么就不可以使用+,-操作。所以,算法必须知道一些关于iterator的信息。所以在iterator这个类里,定义了如下和5个和iterator有关的信息:
template<typename Category,
typename T,
typename Distance = ptrdiff_t,
typename Pointer = T*,
typename Reference = T&>
struct iterator
{
typedef Category iterator_category;
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
};
iterator_category:迭代器的类别,如输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器等。不同类型的迭代器支持的操作不同,在算法优化的时候需要用到。
value_type:迭代器指向的元素的类型,让算法知道容器中存储的数据类型。
difference_type:用于表示两个迭代器之间的距离的类型,通常是某种整数类型。
pointer:指向迭代器所指元素的指针类型。
reference:迭代器所指元素的引用类型。
iterator_category具体如下:
Input Iterator
此迭代器不允许修改所指的对象,即是只读的。支持==、!=、++、*、->等操作。
Output Iterator
允许算法在这种迭代器所形成的区间上进行只写操作。支持++、*等操作。
Forward Iterator
允许算法在这种迭代器所形成的区间上进行读写操作,但只能单向移动,每次只能移动一步。支持Input Iterator和Output Iterator的所有操作。
Bidirectional Iterator
允许算法在这种迭代器所形成的区间上进行读写操作,可双向移动,每次只能移动一步。支持Forward Iterator的所有操作,并另外支持–操作。
Random Access Iterator
包含指针的所有操作,可进行随机访问,随意移动指定的步数。支持前面四种Iterator的所有操作,并另外支持it + n、it - n、it += n、 it -= n、it1 - it2和it[n]等操作。
//五种迭代器型别 struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag : public input_iterator_tag {}; struct bidirectional_iterator_tag : public forward_iterator_tag {}; struct random_access_iterator_tag : public bidirectional_iterator_tag {};
当把内置指针当参数传递给算法后,算法无法得知iterator里定义的iterator_category,value_type,difference_type等信息,算法就无法工作。这时可以加一个中间层,也就是创建一个iterator_traits类,它包装了iterator。(萃取器,萃取iterator的特性)
template<class Iterator>
struct iterator_traits {
typedef typename Iterator::iterator_category iterator_category; //iterator属性1:类别
typedef typename Iterator::value_type value_type; //iterator属性2:指向的对象型别
typedef typename Iterator::difference_type difference_type; //iterator属性3:迭代器距离
typedef typename Iterator::pointer pointer; //iterator属性4:指针
typedef typename Iterator::reference reference; //iterator属性5:引用
};
原生指针在 C++ 中可以用作迭代器,因为它们支持迭代器所需的基本操作,如递增、递减、引用等。然而,原生指针本身并不是一个类类型,它们没有定义,但是标准库中泛型算法和容器希望迭代器都有上面提到的5种特性,但是对于原生指针,并没有任何结构定义。
为了让原生指针能够在这样的泛型代码中无缝使用,标准库提供了 iterator_traits 的特化版本。STL源码针对原生指针T*设计的特化版本萃取机,源码还设计了一个const T*原生指针的特化版本。
//模板偏特化,c++内置指针。
template<typename T>
struct iterator_traits<T *>
{
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};
//模板偏特化,const c++内置指针。
template<typename T>
struct iterator_traits<const T *>
{
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef const T* pointer;
typedef const T& reference;
};
为什么原生指针可以作为迭代器?
在 C++ 中,迭代器是一个抽象概念,用于访问和遍历容器中的元素。迭代器最基本的行为包括移动到下一个元素、移动到上一个元素、访问当前元素等。原生指针自然地支持这些操作,因此可以被当作迭代器使用。
原生指针支持以下操作,这些操作与迭代器的基本要求相符合。
*ptr允许访问指针所指向的值。
递增(++ptr)和递减(--ptr),移动指针到下一个或上一个内存地址,对于连续存储的元素(如数组)来说,这相当于移动到下一个或上一个元素。
加减整数(ptr + n, ptr - n):对于指针来说,可以通过加上或减去一个整数来移动多个位置,这对应于随机访问迭代器的能力。
指针之间的比较(ptr1 == ptr2, ptr1 < ptr2等):可以判断两个指针是否指向同一个位置或者一个指针是否在另一个指针之前,这对于确定遍历的终止条件很有用。
下面是原生指针当作迭代器的例子:
#include <iostream>
#include <vector>
// 泛型求和函数,使用迭代器作为参数
template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type sum(Iterator begin, Iterator end) {
typename std::iterator_traits<Iterator>::value_type total = 0;
for (Iterator it = begin; it != end; ++it) {
total += *it;
}
return total;
}
int main() {
// 使用 std::vector 的迭代器
std::vector<int> v = {1, 2, 3, 4, 5};
std::cout << "Sum of vector elements: " << sum(v.begin(), v.end()) << std::endl;
// 使用原生数组的指针作为迭代器
int arr[] = {5, 4, 3, 2, 1};
std::cout << "Sum of array elements: " << sum(arr, arr + sizeof(arr)/sizeof(arr[0])) << std::endl;
return 0;
}