1 左值与右值
1.1 定义
左值:能用在赋值语句等号左侧的东西,他能代表一个地址。
右值:不能作为左值的值就是右值,右值不能出现在赋值语句中等号的左侧。
c++的任意一条表达式,要么是左值,要么是右值,不可能两者都不是。
一个左值,它可能同时具有左值属性和右值属性。
int test(){
int i = 100;
i = i + 1; //i是左值,虽然它出现在了等号的右边,
// i用在等号的右侧,我们说i有一种右值属性(不是右值)
// i出现在等号的左边,用的是i代表的内存中的地址,我们说i有一种左值属性
}
1.2 用到左值的运算符
(1)赋值运算符
int test(){
int i;
(i = 4) = 8; //i=4为左值,可以赋值
cout << i << endl; //8
}
(2)取地址&
int test(){
int i = 5;
&i;
}
(3)string、vector下标[]都需要左值
int test(){
string abc = "hello";
abc[0];
}
2 引用的分类
主要有三种形式的引用,左值引用、常量引用、右值引用。
2.1 左值引用
将变量绑定到左值的引用即为左值引用,左值引用必须初始化,不存在空引用,且不可以绑定右值。
int test(){
int a = 1;
int &ref = a;
cout << ref << endl; //1
ref = 5;
cout << a << endl; //5
a = 10;
cout << ref << endl; //10 左值引用ref与其绑定的变量a指向同一内存,修改两者任一一个的值,则引用的值与变量的值均改变
// int &ref2; //错误,左值引用必须初始化
// int &ref3 = 2; //错误,左值引用不可以绑定到右值
}
2.2 常量引用
常量引用也是左值引用,和传统左值引用的区别在于常量引用的对象无法改变,同时常量引用也可以绑定到右值。
int test(){
int a = 1;
int b = 2;
const int &ref = a;
a = 3;
cout << ref << endl; //3, ref引用的对象值改变,故ref的值改变
// ref = b; // 错误,不可以将const引用ref指向新的对象
const int &ref2 = 18; // const引用可以绑定到右值
cout << ref2 << endl; //18
}
2.3 右值引用
将变量绑定到右值的引用即为右值引用。系统希望程序员用右值引用来绑定一些即将销毁的或是一些临时的对象上。
右值引用也是引用,可以理解为一个对象的名字。
int test(){
string s1{"hello world"};
string &ref1 = s1; //s1为左值,可以绑定到左值引用
// string &&ref2 = s1; //错误,s1为左值,不可以绑定到右值引用
string &&ref3{"hello world"}; //临时变量被当作右值处理,可以绑定右值引用
const string &ref4{"hello world"}; //const引用绑定到右值
const string &ref5 = s1;//const引用绑定到左值
}
2.4 ++i 与 i++
(1)前置递增递减运算符(++i, --i)
由于在实际计算中,++i先将i+1,再返回i本身,因而返回的是左值。
int test(){
int i = 100;
(++i) = 1000;
cout << i << endl; //1000
}
(2)后置递增递减运算符(i++, i–)
由于在实际计算中,i++是先用一个临时变量记录i,再给i+1,接着返回该临时变量,由于临时变量被当作右值处理,因而后置递增递减运算符返回的是右值。
int test(){
int i = 100;
// (i++) = 1000; //错误,i++返回的是临时变量,是右值,不可以赋值
}
比较
int test(){
int i = 1;
int &ref1 = ++i; //++i,返回左值,ref1相当于i的别名
i += 100;
cout << ref1 << endl; //102
int j = 1;
int &&ref2 = j++; //j++,返回临时变量,右值
j += 100;
cout << ref2 << endl; //1,ref2绑定的是与j相等的临时变量,与j无关
}
2.5 小结
(1)返回左值引用的函数,连同赋值、下标、解引用、前置递增递减运算符(++i),都是返回左值表达式,可以用左值引用绑定。
(2)返回非引用类型的函数,连同算术、关系、位、后置递增递减运算符(i–),都是返回右值表达式,不能用左值引用绑定,但可以使用const左值引用或右值引用绑定到这类表达式。
(3)所有变量都是左值,因为他们本身有地址。
(4)任何函数里的形参都是左值。void func(int i, int &&j)。
(5)临时对象都是右值。
(6)右值引用虽然绑定到了右值,但其本身是变量,是左值。
int test(){
int &&ref1 = 100;
int &ref2 = ref1; //ref1本身为左值,可以绑定左值引用
}
2.6 右值引用的目的
(a)c++11引入,&&代表一种新的数据类型。
(b)提高程序运行效率。把拷贝对象变成移动对象来提高程序运行效率。
3 move
move唯一的作用是将左值强制转换为右值。
int test(){
int i = 100;
// int &&ref = i; //错误,i为左值不可以绑定到右值引用
int &&ref = move(i); //将i强制转换为右值,供ref绑定
cout << ref << endl; //100
i = 200;
cout << ref << endl;// 200,若改变强制转换的左值,此时右值引用的值也会改变
}