之前没有碰到过这类情况,也不知道实际工程中有什么奥妙,先来一个小的测试例子看一下运行结果:
int a = 1;
int b = 2;
int *tmp = &a;
int *p = tmp;// 第二种情况:int *&p = tmp;(此既是指向指针的引用)
p = &b;
*p = 5;
1、测试此时的a, b , *tmp, *p分别是什么: a = 1, b = 5, *tmp = 1, *p = 5;
2、如果是上述第二种情况,即指向指针的引用,那么这些变量又该是什么值呢?答案是:
a = 1, b = 5, *tmp = 5, *p = 5;
这是因为指向指针的引用,不仅改变了指针所指的对象,也改变了指针本身。
下面以Essential C++ P177的代码举例:
该代码是为了删除一个二叉树中等于某个值的节点:
1 void BTnode::remove_value( const int& val, BTnode *& prev ) //二叉有序树中删除节点值_val == val的节点
2 {
3 if ( val < _val ) //往左子树遍历查找
4 {
5 if ( !_lchild )
6 {
7 return; //不在此二叉树中
8 }
9 else _lchild->remove_value( val, _lchild );
10 }
11 else
12 if ( val > _val ) // 往右子树遍历查找
13 {
14 if ( !_rchild )
15 {
16 return; //不在此二叉树中
17 }
18 else _rchild->remove_value( val, _rchild );
19 }
20 else
21 { //哈哈!~找到了~就是你啦!!
22 if ( _rchild ) //看看这个要被删除的节点是否有右孩子
23 { //果然有~~有点小麻烦了~~哎
24 prev = _rchild;
25 if ( _lchild ) //要删除的节点还有左孩子~得想办法把左孩子弄到右子树下面去,这样才能删除我这个节点
26 {
27 if ( !prev->_lchild ) //看看右子树是否有一堆左孩子
28 {
29 prev->_lchild = _lchild;
30 }
31 else BTnode::lchild_lead(_lchild, prev->_lchild);//遍历到右子树最左节点
32 }
33 }
34 else prev = _lchild;
35 delete this; //不用怕删除了本节点以后,我的孩子们连不到我的父辈节点们~~一句prev = _rchild解决了一切~~
36 }
37 }
上面代码中红色部分是最关键的地方,比如下面这棵树:
其中的null是没有节点(我这样加个null是为了让6看起来是右孩子)。
在这个图里,我要删除5节点。若函数中参数是指针,而不是指向指针的引用。那么上述程序,就会造成删除了5节点以后,二叉树断成两部分。(prev = _rchild;这里我们只把prev重新改变为_rchild的值,而并没有改变5这个地址所指向的值,但我们程序中这一句就要让5的地址也改变)
而如果是指向指针的引用,prev = _rchild;这一句就会把5这个地址赋值为6的地址,这样删除5(即delete this;)之后就不会造成二叉树的断链问题。
Essential C++ P177是这么描述的:
为什么我们将prev以一个reference to pointer来传递呢?难道用单纯的pointer传递还不够吗?不,不够!以pointer来传递,我们能够更改的是该pointer所指之物,而不是pointer本身。为了该表pointer本身,我们必须再加一层间接性。如果将prev声明为reference to pointer,我们不但可以改变pointer本身,也可以改变由此pointer指向的对象。