T; T.pushback(1); string s1 = “Hello”; string s2 = \"World\"; const int &m =1;问:
a,b,a+b,a++,++a,p,*p,T[0],100,string(\"Hello\"),s1,s1+s2,m分别是左值还是右值?
答:
a,b是变量,变量可以看做只有运算对象而没有运算符的表达式,变量表达式都是左值。事实上,变量a,b均是长久的,在生命周期结束才被销毁,且我们能够对a,b进行取地址操作。故a,b均为左值。
a+b是临时变量,在该表达式结束时就被摧毁,且不能对其进行取地址操作,因此a+b为右值。
a++的作用机理是先将a的值拷贝到一个临时变量中,然后将这里临时变量加1,最终返回的是这个临时变量,因此a++为右值。
++a的作用机理是在原数据a上直接加1,最终返回的是原来的那个对象(只不过值加了1),因此++a为左值。
p表示的是指向a的指针,它也是长久的,并且我们能对其进行取地址操作,得到的是指向a的指针的地址。因此p为左值。
*p与a等价,也为左值。
T[0]返回容器T中第一个元素的引用,这是一个int型变量,是长久的,并且能对其进行取地址操作,因此T[0]为左值。
100是个常量,在使用过后就会销毁,并且不能对其进行取地址操作,因此100为右值。
string(\"Hello\")与100类似,也是个常量,在使用过后就会销毁,并且不能对其进行取地址操作,因此string(\"Hello\")为右值。
s1是string类型的变量,与a,b类似,是长久的,并且可以进行取地址操作。因此s1是左值。
s1+s2与a+b类似,是临时变量,在表达式结束就被摧毁,并且不能对其进行取地址操作。因此s1+s2是右值。
m是一个常量类型1的const左值引用,但它本身是一个变量表达式,因此m是左值。
二、左值引用和右值引用
左值引用符:&
右值引用符:&&
左值引用不能绑定到右值对象上,右值引用也不能绑定到左值对象上。
由于右值引用只能绑定到右值对象上,而右值对象又是短暂的、即将销毁的。也就是说右值引用有一个重要性质:只能绑定到即将销毁的对象上。
左值、右值引用的几个例子:
[cpp] view plaincopyprint?
1. int i = 42;//如前所述,i是一个左值对象 2. int &r = i;//正确,左值引用绑定到左值对象i 3. int &&rr = i;//错误,右值引用绑定左值对象
4. int &r2 = i * 42;//错误,如前所述i*42是临时变量,是右值,而&r2是左值引用 5. int &&rr2 = i * 42;//正确,右值引用绑定右值对象
int i = 42;//如前所述,i是一个左值对象 int &r = i;//正确,左值引用绑定到左值对象i int &&rr = i;//错误,右值引用绑定左值对象
int &r2 = i * 42;//错误,如前所述i*42是临时变量,是右值,而&r2是左值引用 int &&rr2 = i * 42;//正确,右值引用绑定右值对象
注意:以上绑定规则有一个例外,如果左值引用是const类型的,则其可以绑定到右值对象上。
[cpp] view plaincopyprint?
1. const int &r3 = i * 42;//正确,我们可以将一个const的引用绑定到一个右值对象
上
constint&r3 = i * 42;//正确,我们可以将一个const的引用绑定到一个右值对象上
对于一个左值,若想使用其右值引用,我们可以用move函数:
1. int &&rr3 = std::move(rr1);//正确,显式使用rr1的右值引用
函数返回值分为:值,引用,指针三种。引用是c没有而c++有的,并且因为引用是左值实现了运算符连续运算的基础,而值和引用都不可以。下面我们就来看一下这三种返回方式的各自的特点:
返回值
1. int test1() 2. {
3. int a = 1; 4. return a; 5. }
返回值是最简单有效的方式,他的操作主要在栈上,根据函数栈的特性局部变量a会在函数结束时被删除,为了返回a的值,需要产生a的复制。如果a原子类型这当然也无所谓,但是如果a是大的对像,那么对a的复制将会产生比较严重的资源和性能消耗。注意函数
返回值本身因为没有名称或引用是右值,是不能直接操作的。 2、返回指针
6. int* test2() 7. {
8. int *b = new int(); 9. *b = 2; 10. return b; 11. }
返回指针是在C中除了返回值以外的唯一方式,根据函数栈的特性也会产生复制,但是这个复制只是4(8)字节的指针,对于返回大型对像来说确实能够减少不少的资源消耗。但是返回指针资源的清理工作交给了调用者,这某种意义上违反了谁申请谁销毁的原则。指针也是右值同样无法操作。 3、返回引用
1. int& test2() 2. {
3. int *b = new int(); 4. *b = 2; 5. return *b; 6. }
引用是C++中新添加的概念,所以返回引用也是C++中相对于C来说所没有的。引用是值的别名,和指针一样不存在对大对像本身的复制,只是引用别名的复制。引用是左值,返回引用可以直接操作,也就可以进行连续赋值,最经典的示例是拷贝构造函数和运算符重载一
般返回引用。
1. test2()+=1;
但是,返回引用会带来一个问题,那就是返回局部变量内存空间,会产生异常,也就是说局部变量是不能作为引用返回的。局部指针可以作为引用返回但是和返回指针一样需要调用者自己去清理内存。