内容学习自:爱编程的大丙
一、右值
lvalue是loactor value不是left value,rvalue是read value不是right value。
- 左值是指储存在内存中,有明确储存地址(地址可取)的数据
- 右值是指可以提供数据值的(地址不可取)数据
便捷区分法:
//左值
int num = 9;
//左值引用
int& a = num;
//常量左值引用
const int& c = num;
//右值引用
int&& b = 8;
//常量右值引用
const int&& d = 8;
二、右值引用
无论声明左值引用还是右值引用都必须初始化,因为引用只是一个别名,并没有对象的内存空间。
在使用右值引用的时候,其实是延长了这个右值的生命周期,而不是将其赋值给另一个左值。不会占用更多的空间。
注意几点:
- 左值初始化右值引用是不违法的。
- 右值不能给普通左值引用赋值。
- 常量左值引用是一个万能引用,他可以接受左值,右值,常量左值和常量右值。
int back()
{
return 1;
}
int a = 1;
//第一条 error
int&& b = a;
//第二条 error
int& c = back();
//第三条
const int& d = back();
const int& e = 1;
三、应用
在 C++ 中在进行对象赋值操作的时候,很多情况下会发生对象之间的深拷贝,如果堆内存很大,这个拷贝的代价也就非常大,在某些情况下,如果想要避免对象的深拷贝,就可以使用右值引用进行性能的优化。
class test14
{
public:
test14() : num(new int(1))
{
cout << "基础构造" << endl;
}
test14(const test14& t) : num(new int(*t.num))
{
cout << "拷贝构造" << endl;
}
~test14()
{
delete num;
}
int* num;
};
test14 back_1()
{
test14 t;
return t;
}
void main()
{
test14 t = back_1();
cout<<"num = "<<( * t.num )<< endl;
}
在back_1()函数中先是创建了一个对象,并调用了基本构造函数,然后看到在main中进行了一个将亡值的赋值操作,因为存在指针所以进行了一个深拷贝。但是这个对象没有使用,就进行了一个赋值,相当于多调用了额外的空间来处理这件事。如果想要节省这个空间和时间,可以使用右值引用。右值引用具有移动语义,移动语义可以将资源(堆、系统对象等)通过浅拷贝从一个对象转移到另一个对象,这样就能减少不必要的临时对象的创建、拷贝以及销毁,可以大幅提高 C++ 应用程序的性能。
class test14
{
public:
test14() : num(new int(1))
{
cout << "基础构造" << endl;
}
test14(const test14& t) : num(new int(*t.num))
{
cout << "拷贝构造" << endl;
}
//添加移动构造
test14(test14&& t) : num(new int(*t.num))
{
t.num = nullptr;
cout<<"移动构造"<<endl;
}
~test14()
{
delete num;
}
int* num;
};
test14 back_1()
{
test14 t;
return t;
}
void main()
{
test14 t = back_1();
cout<<"num = "<<( * t.num )<< endl;
}
见移动构造参数为右值引用,在这个过程只进行了浅拷贝,没有动到指针,而是在拷贝后将原指针置nullptr。移动构造中使用了右值引用,会将临时对象中的堆内存地址的所有权转移给对象t,这块内存被成功续命,因此在t对象中还可以继续使用这块内存。
- ?对于需要动态申请大量资源的类,应该设计移动构造函数,以提高程序效率。需要注意的是,我们一般在提供移动构造函数的同时,也会提供常量左值引用的拷贝构造函数,以保证移动不成还可以使用拷贝构造函数。
四、&&的特性
还没看
|