关于迭代器( iterator ) ,我们知道,所有 标准库容器 都可以使用迭代器(string 不是标准库容器,也支持迭代器)。
类似于指针类型,迭代器提供了对 对象的间接访问。
今天(2018-4-18),我想测试一下 map 的两个特性:
- map 里的元素,都是按关键字排序的。如果关键字是vector<int>,还好不好使?
- 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
比如说指针:
- 指向常量的指针(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 - 常量指针/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 (中文版)