关于迭代器( iterator ) ,我们知道,所有 标准库容器 都可以使用迭代器(string 不是标准库容器,也支持迭代器)。

类似于指针类型,迭代器提供了对 对象的间接访问。

今天(2018-4-18),我想测试一下 map 的两个特性:

  1. map 里的元素,都是按关键字排序的。如果关键字是vector<int>,还好不好使?
  2. map 里的元素关键字不可重复。如果关键字是vector<int>,v1{1 , 2}和v2{1, 2} 和v3{2, 1}是否一样?

看程序:

// mvector.cpp
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

int main(){
	map<vector<int> ,string> m;
	vector<int> vi1;
	vi1.push_back(2);
	vi1.push_back(1);
	m.insert(make_pair(vi1,"cx"));

	vector<int> vi2;
	vi2.push_back(1);
	vi2.push_back(2);
	m.insert(make_pair(vi2,"mitre"));
	cout<<"m.size :  "<<m.size()<<endl;
	
	vector<int> vi3;
	vi3.push_back(3);
	vi3.push_back(4);
	sort(vi3.begin(),vi3.end());
	m.insert(make_pair(vi3,"hello"));
	cout<<"m.size :  "<<m.size()<<endl;

	vector<int> vi4;
	vi4.push_back(4);
	vi4.push_back(3);
	sort(vi4.begin(),vi4.end());
	m.insert(make_pair(vi4,"world"));
	cout<<"m.size :  "<<m.size()<<endl;

	map<vector<int>,string>::iterator mi=m.begin();
	while(mi != m.end()){
		//1
		//vector<int>::iterator vi=mi->first.begin();
		//while(vi != mi->first.end()){
		
                //2
		vector<int>::const_iterator vi=mi->first.begin();
		while(vi != mi->first.end()){
		
                //3	
		//vector<int> v=mi->first;
		//vector<int>::iterator vi=v.begin();
		//while(vi != v.end()){
		
                //4
		//auto vi=mi->first.cbegin();
		//while(vi !=mi->first.cend()){
		cout<<*vi<<' ';
			++vi;
		}
		cout<<" "<<mi->second<<endl;
		++mi;
	}
	return 0;
}

输出结果:


分析:

可以看出,vector<int> 作为map 的关键字,如果两个vector相等(排序后的 vi3==vi4 ),那么map.insert只会插入一个vecotr对象。


注意到程序中的注释1, 2, 3, 4 ,注释1 在编译时有以下错误( 其他三个都能得到正常输出 ):

-----

mvector.cpp:40:43: error: conversion from ‘std::vector<int>::const_iterator {aka __gnu_cxx::__normal_iterator<const int*, std::vector<int> >}’ to non-scalar type ‘std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}’ requested

   vector<int>::iterator vi=mi->first.begin();

-----

原来是我把 const_iterator 赋值给了 iterator 导致了这个错误!!!!

map 中 元素的类型(value_type) 是 pair<const key_type, mapped_type> ,关键字类型是const 修饰的!!!

在我的程序中,map 的关键字是 const vector<int> ,那么这个vector 的迭代器是const_iterator 。在注释1 中,直接把const_iterator 赋值给了 iterator ,编译出错了!!!

这里的const_iterator 相当于 底层const(low-level const)指针,不能把一个底层const的 指针赋值给一个普通指针。(这很好理解,如果能赋值的话,这个普通指针就可以改变 const对象 的内容了!!)

----------------------------------------------------

补充说明const 限定符[1]

如果我们想定义一个变量,变量的值不能被改变,就可以用const ,用const修饰的也变量称为常量。

如:

const  int  coni =  100;   // 这个 coni 的值不能被改变!!

int j = coni ;    // 可以把 整型常量coni 赋值给 变量j 。作为右值,只是取其值,又不会改变,当然可以!

指针/引用 和 const 在一起的时候,就出现了 所谓的顶层const 和 底层const 

比如说指针:

  1. 指向常量的指针(pointer to const),不能改变指向对象的值。
    如:
    const  double  pi = 3.14 ;       // pi 是 double常量
    const  double  *cptr = &pi ;    // 指针cptr是指向常量的指针!! 这里指向 double常量 pi 
    指向常量的指针/引用,她们可以指向非常量。只是不能通过指针/引用 改变所指对象的值。(指向常量的指针:虽然我可以指向非常量,但是不能通过我改变所指对象的值,因为我是指向常量的指针,哈哈哈~~~)
    如:
    double  pi2 = 3.14 ;     // pi2 是一个变量,pi2 可以改变
    cptr = &pi2 ;         // 现在 cptr 指向 非常量pi2,指可以,但是不同通过*cptr 改变 pi2
  2. 常量指针/const 指针 (const pointer),const 指针不能改变所指对象(指针里的地址不能变)。
    如:
    int  n = 0 ;   
    int  *const  pn = &n ;  // pn 将只能指向 n,const 指针pn 的值不能改变
    const  double  *const  cpc = &pi ;   // cpc 是一个指向常量对象的常量指针

注:

  • 指针本身是对象,她可以指向另外一个对象
  • 我们都知道指针的类型必须和所指对象的类型一致,但有两个例外
      1.在指向常量的指针中已经说了,指向常量的指针 可以指向非常量对象
           2.父类指针 可以指向 子类对象
  • 引用不是对象


底层const(low-level const):指向常量的指针对常量的引用(引用的const都是底层的)。

不是底层const ,就是顶层const。

顶层const(top-level const):定义的对象是常量(对于指针对象,指针本身是常量;对于其他对象,对象本身是常量)

举个栗子:

const int ci = 0 ;  //ci 值不能改变, 定义的ci对象本身就是常量, 顶层
int *const p1 = &ci ; //p1 的值不能变,定义的p1指针本身就是常量(常量指针),顶层

//底层const 的必要条件,定义的是指针/引用

int i = 0;
const int *p2 = &ci ; // p2 是指向常量的指针,底层 。注:指向常量的指针 可以 指向非常量
const int &r = ci ; // r是对常量的引用,底层。 注:对常量的引用 可以 引用非常量

const int *const p3=p2;  //靠左为底层const, 靠右为顶层const


 作为右值 (在赋值= 的右侧) :
顶层,取其值没有任何问题
底层,要求左值必须 也是底层的const。(如果底层const 赋值给非底层const,那么通过赋值,我们就可以改变 ”被指向对象“ 的值,这显然是不合理的)

----------------------------------------------------

注释1 的错误很明显了!

注释2 纠正了注释1 的错误,正确。

注释3 和注释1 对比:验证了 补充说明 里的最后两句话。

关键字类型是const vector<int> , 我们可以把这个顶层const 赋值 给一个变量;再对变量操作,就没有底层const_iterator 的限制了。

注释4用到了C++11 特性(auto,cbegin,cend)。


References:

[1]  C++ Primer , 5E (中文版)



Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐