一、左值
1.1、什么是左值?什么是左值引用?
一般请何况:左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。 特殊请何况:定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
代码如下:
int main()
{
int* p = new int(0);
int b = 1;
const int c = 2;
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;
return 0;
}
二、右值
2.1 什么是右值?什么是右值引用
右值也是一个表示数据的表达式,如:字面常量、表达式返回值,传值返回函数的返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。
代码如下:
int main()
{
double x = 1.1, y = 2.2;
10; x + y;
fmin(x, y);
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
10 = 1; x + y = 1;
fmin(x, y) = 1;
return 0;
}
需要注意的是:右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定的位置,这样就可以取到该位置的地址。 例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用。
int main()
{
double x = 1.1, y = 2.2;
int&& rr1 = 10;
const double&& rr2 = x + y;
rr1 = 20;
rr2 = 5.5;
return 0;
}
三、左值引用和右值引用的比较
3.1 左值引用总结
- 左值引用只能引用左值,不能引用右值
- const左值引用既可以引用左值,也可以引用右值
int main()
{
int a = 10;
int& ra1 = a;
const int& ra3 = 10;
const int& ra4 = a;
return 0; }
3.1 右值引用总结
- 右值引用只能右值,不能引用左值
- 右值引用可以move以后的左值
int main()
{
int&& r1 = 10;
int a = 10;
int&& r2 = a;
int&& r3 = std::move(a);
return 0;
}
四、右值引用的应用
左值引用的短板:
当我们的函数返回对象是一个局部变量时,出了作用域就不存在了,就不能使用左值引用返回只能使用传值返回,传值返回会导致至少使用一次拷贝构造(深拷贝),在一些旧一点的编译器中有可能是两次拷贝构造,一些新一点的编译器会进行优化。
4.1右值引用中的移动语义
移动构造
移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己。
string(string&& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
cout << "string(string&& s) -- 移动语义" << endl;
swap(s);
}
int main()
{
bit::string ret2 = bit::to_string(-1234);
return 0;
}
移动构造中不会新开空间去进行拷贝数据,所以就会提高效率。
移动赋值
在bit::string类中增加移动赋值函数,再去调用bit::to_string(1234),不过这次是将bit::to_string(1234)返回的右值对象赋值给ret1对象,这时调用的是移动构造。
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动语义" << endl;
swap(s);
return *this;
}
int main()
{
bit::string ret1;
ret1 = bit::to_string(1234);
return 0;
}
|