我们用T来表示类型,这里的类型可以是系统内置类型,也可以是用户自定义类型。
我们先来熟悉一下这种表示方法。这样的表示在模板中是很常见的。
const T* 表示指针指向的是一个T类型的变量,该指针具有底层const属性,也就是说通过该指针,只能读取指向对象的值,但不能修改其指向对象的值。
同理,const T&表示常引用,所引用的也是T类型的也是底层const,也就是说通过该引用,只能读取指向对象的值,但不能修改其指向对象的值。
热身运动完毕。开始进入正题。
我们知道,对于函数形参,有三种传参方式,传值调用、传指针调用、传引用调用。
对于传指针调用,实际用的也是传值方式,只不过传的是指针的值。
那么问题来了,如果我们既想修改指针所指对象的值,又想修改指针本身的值,怎么做?
C功底好的同学马上就会说,来个二维指针就搞定了。是的,确实可以。不过,对于C++来说,还有一种方法也可以做到。那就是指针的引用。
T data;
T* pData = &data;
T* &myRef = pData;
在这里myRef就是指针pData的别名。
我们可以将T用int替换,来验证一下。
int data;
int* pData = &data;
int* &myRef = pData;
cout << "addr of data: " << &data << endl;
cout << " pData: " << pData << endl;
cout << " myRef: " << myRef << endl;
cout << "addr of pData: " << &pData << endl;
cout << "addr of myRef: " << &myRef << endl;
输出结果为:
addr of data: 0x22fef8
pData: 0x22fef8
myRef: 0x22fef8
addr of pData: 0x22fef4
addr of myRef: 0x22fef4
可以清楚地看到,myRef和pData是同一个变量的两个名字。
可以这样记忆,T* &r,声明一个变量r,这个r是一个引用,指向T*类型的一个指针。
好的,介绍完T*&,下面我们进更进一步,介绍一下const T*&。
第一个闯入大脑中的问题,肯定是这个const是修饰谁的。换句话说,有两种理解方式。
第一种理解是:这是一个普通指针的const引用。第二种理解是:这是一个普通引用,指向一个const指针。
对于const T*& myRef; 来说,第二种理解是正确的,const T*& 和 T const*& 产生相同的结果,const T*& 和 T const*& 是等价的,都表示一个普通引用,指向一个const指针。
也就是说,const是修饰指针的。
如果想定义一个指向普通指针的const引用,则应该按照下面的方式定义:
T* const& myRef = pData;
我们知道,引用天生具有顶层const属性,也就是说,引用一经初始化,就无法再指向其他变量。
所以不存在T* & const myRef = pData;
经过上面的介绍,指向const指针的const引用怎么表示呢?
答案是:T const* const& 或 const T* const&
好,现在我们再结合typedef来练习一下。
定义 typedef T* T_ptr;
那么请问 const T_ptr& 表示什么意思?
答案是:T* const&,即T*的const引用,该引用是T*类型的指针的别名。
关于类型转换
int data;
int* pData = &data;
const int* & myRef2 = pData;
const int** myPtr = &pData;
error: invalid initialization of reference of type ' const int*& ' from expression of type ' int* '
error: invalid conversion from ' int** ' to ' const int** ' [-fpermissive]|
const int* & myRef2 = pData; 这一句出错。
const int** myPtr = &pData;这一句出错,表明不存在从int** 到const int**的隐式转换。
这里只讲第一个错误,对于第二个错误,我们单独开一篇博文来讲,因为那是个相当大的问题,足够重开一篇博文来讲述了。
第二个问题的相关讨论在这里 : http://bbs.csdn.net/topics/40321011
对于第一个问题:const int* & myRef2 = pData; 这一句出错,原因很简单,因为我们定义的是const指针的引用,普通引用是一定要和指向的变量类型完全一致的。如果类型不一致,编译器会自动进行类型转换,生成一个类型符合要求的中间变量,并让引用指向中间变量,中间变量为右值。而普通引用不能指向右值的,const引用则可以。我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。而const引用就可以,说的就是这个道理。
再强调一下,虽然const引用可以,但是此时,const引用指向的是一个临时变量。
所以修改方法有两个:
法一:
把const int* & myRef2 = pData; 修改为 int* & myRef2 = pData; 即 去掉const
法一是把类型修改一致,此时myRef2就是pData的别名。
法二:
把const int* & myRef2 = pData; 修改为 const int* const& myRef2 = pData;
法二是把引用改为const引用,此时,
如果不相信myRef2是中间变量的别名,而不是pData的。我们可以做下面这个实验。
int data;
int* pData = &data;
const int*const & myRef2 = pData;
cout << "addr of myRef2: " << &myRef2 << endl;
cout << "addr of pData: " << &pData << endl;
输出结果为:
addr of myRef2: 0x22fef8
addr of pData: 0x22fef0
显然,此时的myRef2根本不是pData的别名。
Done!