C++的& 与 *区别(从递归上解释)

来源:互联网 发布:使用js完成九九乘法表 编辑:程序博客网 时间:2024/06/16 18:41

在刷leetcode时候看到一道题目,里面所使用到一个特殊的类型为

TreeNode* &prev

TreeNode表示一个树的结构体的类型,但是却取了* &,着实让人奇怪,因为这个原因,特地复习一下c++的引用&
关于c++的引用& 和*的资料,我觉得这篇博客中已经解释的相当清楚了,故在此放上这个博客的链接:
http://www.cnblogs.com/Mr-xu/archive/2012/08/07/2626973.html
简单说一下我对引用和指针的区别的理解吧,首先引用是对一个变量的一个别名,别名的意思就是说这个变量可以代表这个变量,主要作用是体现在函数传参的时候,利用引用,可以在函数传参的时候,对实参进行修改,并且不会产生多余的内存来存放形参,在传递大块内存的对象(形参会调用对象的拷贝构造函数)或者数据的时候,用引用比直接传递形参要有效的多,并且可以直接对实参进行操作,&和*的区别,一方面是不用指针使得代码更利于维护,另一方面直接对实参进行操作,使代码更利于理解,但这些好像不会对代码的运行造成实际的影响,下面这个递归的例子就会看出有什么影响了。
对了,在查询引用的例子的时候,本人也研究了下c++的临时对象的内容,在这里贴上临时对象比较好的例子(但是里面有些代码并不能成功运行,需要进行修改):
http://blog.csdn.net/waljl/article/details/51144351
其中,总的来说,临时对象的作用范围,类似于形式参数,只用temp来保存一下对象处理的中间值,在生成临时对象的最大表达式结束处之后,临时对象就会释放(这也是为什么很多指向临时对象的引用和指针出现野指针和空引用的罪魁祸首)
以下是我所展示的一段代码,表示了一部分

int main(int argc, const char * argv[]) {    string a="hello";    string b="world";    char temp_2[20];    const char* temp=(a+b).c_str();//    temp_2=(a+b).c_str();    strcpy(temp_2,(a+b).c_str());    printf("%s\n",(a+b).c_str());    printf("%s\n",temp);    printf("%s\n",temp_2);    return 0;}

这里面所有的(a+b),运行时都会出现(a+b)的临时对象。那么,在编译的时候只有那句打注释的语句错误,其他的输出结果相同,看来有不少编译器自己就提示出了这个错误,这个错误就是因为,当temp_2指向(a+b).c_str()的之后,(a+b)这个临时对象释放,执行其析构函数,temp_2成了野指针,引发了错误。所以,使用strcpy才是正确的用法。

接下来回到正题:
&与*的区别,以下是一段判断一棵树是否为二叉平衡树的函数

/** * Definition for a binary tree node. * struct TreeNode { *     int val; *     TreeNode *left; *     TreeNode *right; *     TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */class Solution {//private://    TreeNode* prev;public:    bool isValidBST(TreeNode* root) {        //通过正序的遍历,得到的将是一个递增的序列,并且保存前一个指针和后一个指针相互判断,必须一直保持前一个值比后一个小        //利用引用来将prev保存为全局变量,使得每次都能保存prev的地址,来实现前一个指针prev和后一个指针root一直比较,不受到递归的影响        TreeNode* prev = NULL;        return isBST(root,prev);    }    bool isBST(TreeNode* root,TreeNode* &prev)    {        if(root==NULL) return true;        if(!isBST(root->left,prev)) return false;        if(prev!=NULL && (prev->val)>=(root->val)) return false;        prev=root;        return isBST(root->right,prev);    }};

判断BST的基本方法,BST进行in-order遍历出来序列是否为递增序列,也就是利用在递归遍历树的时候,是否总是保持前一个节点的值小于当前节点的值,那么为了一直使得prev保存的是前一个节点地址,为什么要用TreeNode* &prev,而不用TreeNode* prev?
答案就是在这里加上了&引用符号,形成TreeNode* &prev,是为了让TreeNode* prev变成全局变量,不让prev受到递归的影响,而不能一直表示前一个值的地址,如果表示成TreeNode* prev,则如果输入的树为
这里写图片描述
因为刚开始输入的prev为NULL,在最后一次递归结束的时候,prev也为NULL,无法实现(prev->val)>=(root->val)的比较,则返回错误的true.
然而,使用TreeNode* &prev,由于是对节点的TreeNode* prev的引用,所以递归的时候不会出现形式参数TreeNode* prev,都是对实参TreeNode* prev操作,相当于TreeNode* prev变成了全局变量,不会因为递归的形参而影响。那么,prev就可以一直保存前一个节点的值了,都会进行(prev->val)>=(root->bal)的比较了。

相同的做法,可以将这个引用的值变成成员变量或者我考虑用二级指针(不过二级指针降低了可读性)也可以,不过在OJ中不断调用这个对象的成员对象无法预测未来时候出现问题。所以,最好的解决办法还是用引用来使其变成全局变量

原创粉丝点击