const iterator 和const_iterator的区别

这是个困扰我很久了的问题,可能一开始对面向对象的理解不够深。

刚刚想明白了,随手记录一下。


  • 先从const iterator和const_iterator说起

const iterator 是iterator本身是个常量,iterator本身里面存的是指针,也就是iterator的值,也就是那个指针不能改变,也就是不能指向其他的位置,但是所指向的位置的元素是可以通过这个iterator来改变的。

const_iterator 其实本质来说,是另一个类。我们可以想象成,它的数据成员是一个指向常量元素的指针,(比如 const T*)也就是说,这个const_iterator里存着的指针是可以改变的,即可以 ++ 或 - - 操作,但是,这是一个指向常量的指针,指向的元素是常量,不可改变。

  • 有点绕,我们再从一个例子扒一扒。
vector ivec1(10);
const vector ivec2(10);
vector::iterator iter1 = ivec1.begin(); // true
vector::iterator iter2 = ivec2.begin();  // error
vector::const_iterator iter3 = ivec2.begin(); // true
vector::const_iteartor iter4 = ivec1.begin();  // true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通过上面这个例子,我们就可以更直观的再深入理解一下了。 
因为ivec2本身就是一个常量的vector,所以ivec2里面的元素必然是不能被改变的。如果直接定义一个iterator是会报错的,因为iterator意味着这个iterator可以遍历容器,且可以改变容器元素,显然如果容器被定义成常量了之后,这个iterator是不合理的。

所以这才有了const_iterator的出现。作为一个迭代器,遍历元素是必须要的,不然就丧失了一个迭代器的意义了。但是由于常量容器的存在,iterator不能满足这个需求。const_iteartor代表的就是不能改变容器元素,但是可以遍历容器的迭代器。

但是const_iterator不仅仅是只针对已经被声明为常量的容器用的,如果一个非常量容器,但是你不想改变容器的元素,那么也可以用const_iterator,因为里面存的是一个指向常量的指针。

  • 最后再来看一下底层实现,更深入的扒一扒。 
    直接看最简单的vector容器吧,list和deque要更加复杂一些,但是本质都是一样的。(list是指向node节点的指针,duque的iterator更加复杂,因为它的内存分配是一段一段的,详情可见STL源码或者我的github www.github.com/linxiaoye/TinySTL)
/*vector的数据结构一般是这样的*/
T* start;
T* finish;
size_t n;

typedef T            value_type;
typedef T*           iterator;
typedef const T*     const_iterator;

iterator begin() { return start; }     // ①
const_iterator begin() const { return start; }  // ②
const_iterator cbegin() const { return start; }  // ③
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

我们可以看到,vector里面是这样实现的。 
begin()有两个实现,外加一个cbegin(),有三个实现。 
①就是最一般的实现,可遍历,可改变元素值。一般用于非常量容器。 
②是可遍历,不可改变元素值,一般用于常量容器。当你对一个常量容器直接调用begin(),就是调用的②。调用值得一提的是,它这个不可改变元素值是全方位表现出来了的。从 typedef const T* const_iterator 开始,就限制了这是一个指向常量的指针,不可改变元素值;②中返回值是const_iterator也说明了返回的是一个存有指向常量的指针的迭代器;cosnt成员函数也说明了,不能通过这个函数来改变元素的值。 
③其实跟②是几乎一样的,只是函数名不同,也就是说,当你实例化一个常量vector时,你调用begin() 和 cbegin()其实是一样的,没有区别。但是,当你定义的是一个非常量容器时,你想调用一个不能改变所指向元素的迭代器,那么就应该调用cbegin(),它可以满足你的要求。

 


  • 最后总结一下 
  • iterator 可遍历,可改变所指元素 
  • const_iterator 可遍历,不可改变所指元素 
  • const iterator 不可遍历,可改变所指元素 
  • const_iterator 主要是在容器被定义成常量、或者非常量容器但不想改变元素值的情况下使用的,而且容器被定义成常量之后,它返回的迭代器只能是const_iterator 
  • begin() 与 cbegin() 其实在容器被定义成常量之后,本质上是一样的,没有区别,调用哪个都行;但是容器时非常量时,如果你不想改变元素值,就只能调用cbegin()

你可能感兴趣的:(C++,const,iterator,const_iterator)