首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

认识左值与常引用

认识左值与常引用

1.左值的定义左值是(lvalue)是C++中的一个基本概念。凡是可以出现在赋值运算符左边的表达式都是左值。与左值相对的就是右值(rvalue),凡是可以出现在赋值运算右边的表达式都是右值。左值一定可以作为右值,但反过来不一定成立。
可以给左值下一个定义:值为可寻址的非只读单元的表达式称为左值。
因此,理解左值的概念,需要注意一下几点:
(1)左值一定是可以寻址的表达式,不能寻址的表达式不能作为左值。例如,表达式3+5是一个符号常量表达式,它不能被寻址,因此就不能作为左值。
(2)常变量虽然可以寻址,但是由于只读的限制,也不能作为左值。
(3)如果表达式的运算结果是一个内置类型的临时无名对象,则表达式不能作为左值,如下面的例子。
#include <iostream>using namespace std;int func(){    return 0;}int main(int argc,char* argv[]){    int i;    i+1=5;    //statement1    func()=5; //statement2}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
以上程序中statement1和statement2均是非法语句,原因是i+1的运算结果是一个内置类型的临时无名对象,函数func()的返回值也是一个内置类型的临时无名对象,所以他们都不能作为左值。注意,这里的临时无名对象指的是没有任何标识符与之关联的数据实体,包括类对象和基本数据类型的实体。
(4)如果表达式的运算结果是一个引用,则此表达式可以作为左值,如下面的例子。
#include <iostream>using namespace std;int& func(){    int a=0;    return a;}int main(int argc,char* argv[]){    int i=1;    (i+=1)=5;          //statement1    cout<<" "<<i<<endl; cout<<(func()=6);  //statement2    getchar();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
程序的输出结果是:5 6。在statement1中,由于表达式i+=1的运算结果是对i的引用,所以它也可以作为左值。而在statement2中,函数调用func()的返回结果是对局部变量a的引用,所以该表达式也可以作为左值。
由此可以,并不是只有单个变量才能作为左值,也不能仅由表达式的外在形式判断它是否为左值。要根据一个表达式的运算结果的性质进行判断。
2.建立引用的条件由于引用变量中实际上存放的是被引用对象的地址,所以,左值一定可以建立非常引用,对非左值建立常引用,首先要考虑该表达式结果是否能寻址,其次还要考虑表达式结果的数据类型与引用数据类型是否一致,只有在满足了这两个条件的基础上,才能将表达式结果的地址送入引用变量。否则,只能另外创建一个无名变量,该变量中存放非左值表达式的运算结果,然后再建立对该无名变量的常引用。
在C++语言中,经常把函数的参数声明为引用,这样在发生函数调用时可以减少运行时的开销。但要特别注意的是,将函数的参数声明为一般的引用还是声明为常引用,是有讲究的。在应该讲函数的参数声明常饮用的时候,却把它声明为一般的引用,很有可能造成函数无法正常使用。考察如下程序。也可以查看我的另一篇详述该内容的blog。
临时变量作为非const的引用进行参数传递引发的编译错误
#include <iostream>using namespace std;int Max(int& a, int& b){    return (a>b)?a:b;}int main(int argc,char* argv[]){    int i=2;    cout<<Max(i,5)<<endl; //编译出错    getchar();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
这个程序无法通过编译,函数调用Max(i,5)中,由于5不是左值,不能为它建立引用,所以出现编译错误。在这种情况下,必须修改函数Max()的定义,也就是把它的参数声明为常引用:int Max(const int& a, const int&b),这样就解决问题了。可见,将函数的参数声明为常引用,不完全是因为参数的值在函数体内不能修改,还考虑了接受非左值作为函数实参的情况。
3.常引用的特殊性质对某个变量(或表达式)建立常引用时,允许发生类型转换,而一般的引用则不允许,见下面的程序。
#include <time.h>#include <iostream>using namespace std;int Max(const int& a, const int& b){    return (a>b)?a:b;}int main(int argc,char* argv[]){    char c='a';    const int &rc=c;    cout<<(void*)&c<<endl;    cout<<(void*)&rc<<endl;    int i=7;    const int &ri=i;    cout<<(void*)&i<<endl;    cout<<(void*)&ri<<endl;    cout<<Max(rc,5.5)<<endl;    getchar();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
程序输出结果如下:

在这个程序中,如果将语句const int &rc=c;中的const去掉,将发生编译错。原因是一般的引用只能建立在相同数据类型的变量上。同样,之所以允许Max(i,5.5)这样的函数调用,也是因为函数Max()的第二个参数是常引用,因此可以将实参5.5先转换为int类型的无名变量,然后再建立对该无名变量的常引用。
所以,对一个表达式建立常引用时,如果该表达式的结果可以寻址,并且表达式的数据类型与引用类型相同,那么可以直接将该表达式结果的地址送入引用变量。此例中,&i和&ri的值相等就说明了这一点。否则,若表达式的数据类型与引用类型不相同,或是表达式结果不可寻址,那么只能另外建立一个无名临时变量存放表达式的结果(或其转换后的值),然后将引用于无名临时变量绑定,此例中&c与&rc的值不同正好说明了这一点。
继承事业,薪火相传
返回列表