一、左值和右值
左值:在内存中有确定存储地址、有变量名,表达式结束后依然存在的值。左值就是非临时对象。
右值:在内存中没有确定存储地址、没有变量名,表达式结束后就会被销毁的值。右值就是临时对象。
左值引用:绑定到左值的引用,通过&
来声明。
右值引用:绑定到右值的引用,通过&&
来声明
例子 :
int a=10;//a是左值,10是右值
int b=a; // 这里的b和a都是左值
int &c = a;//a是左值,左值引用
int &&d = 10;//10是右值,右值引用
ps:通常为了提升程序的性能,在传参时,使用指针和引用提高效率,减少系统的开销,引用本质上就是指针,只不过再次封装了一层,提高代码的稳定性。因为引用初始化必须要赋值,否则会报错,而指针初始化可以不用赋值但是会乱飞,虽然简单,b=a这个操作,本质上是将a先拷贝一份,然后再将值赋给b,0拷贝的做法是,直接赋值地址的,没有拷贝操作。
int a = 10;
int *b = &a;
二、智能指针资源转移(c++11)
C语言中,需要手动去转移资源,并且需要手动去释放资源:通过指针变量的拷贝,然后释放原来的指针
int* ptr1 = malloc(sizeof(int));
*ptr1 = 10;
int* ptr2 = NULL;
// 转移资源
ptr2 = ptr1;
ptr1 = NULL;
// 现在ptr2拥有这块内存,ptr1为NULL
if (ptr2 != NULL) {
printf("%d\n", *ptr2);
free(ptr2); // 正确释放内存
}
C++中,智能指针是c++11引入的新特性,帮助我们自动管理内存的,unique_ptr包含不能被共享的资源,智能指针对象不能拷贝但可以移动。
//---------------------方法一-------------------------
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
std::unique_ptr<MyClass> ptr2;
ptr2 = std::move(ptr1); // 使用std::move将ptr1转换为右值
//---------------------方法二-------------------------
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
std::unique_ptr<MyClass> ptr2;
ptr2 = ptr1; // 使用std::move将ptr1转换为右值
方法二会在编译的时候报错,因为智能指针没有delete函数,直接拷贝编译不通过,需要使用std::move转移资源
三、本质是什么(本质是将左值转换为右值)
如下所示,常量右值引用只能等于右值,等于左值会编译报错,需要使用std::move将左值转换成右值就不会报错了,unique_ptr也是左值的一种,需要使用move转移资源
int a = 10;
const int a1 = 10;
const int a2 = 20;
//非常量右值引用
int &&b1 = a; //错误,a是非常量左值,不能绑定到非常量右值引用
int &&b2 = a1; //错误, a1是常量左值,不能绑定到非常量右值引用
int &&b3 = 10; //正确,10是纯右值,可以绑定到非常量右值引用
int &&b4 = a1+a2; //正确,(a1+a2)是纯右值,可以绑定到非常量右值引用
//常量右值引用
const int &&c1 = a; //错误,a是非常量左值,不能绑定到常量右值引用
const int &&c2 = a1; //错误, a1是常量左值,不能绑定到常量右值引用
const int &&c3 = a+a1; //正确,(a+a1)是纯右值,可以绑定到常量右值引用
const int &&c4 = a1+a2; //正确,(a1+a2)是纯右值,可以绑定到常量右值引用
//正确做法
int a = 10;
const int a1 = 10;
//非常量右值引用
int &&d1 = std::move(a); //正确,可以绑定到非常量右值引用
int &&d2 = std::move(a1); //错误,不能绑定到非常量右值引用
//常量右值引用
const int &&c1 = std::move(a); //正确,可以绑定到常量右值引用
const int &&c2 = std::move(a1); //正确,可以绑定到常量右值引用
使用std::move后原来的对象就变成未知状态,如int a;这个状态,仅有分配的地址,又没存放值