左值右值移动语义
1. 左值和右值的概念
左值与右值这两个概念是从 C 中传承而来的,左值指既能够出现在等号左边,也能出现在等号右边的变量;右值则是只能出现在等号右边的变量。
int a; // a 为左值
a = 3; // 3 为右值
- 左值是可寻址的变量,有持久性;
- 右值一般是不可寻址的常量,或在表达式求值过程中创建的无名临时对象,短暂性的。
左值和右值主要的区别之一是左值可以被修改,而右值不能。
2. 右值引用和移动语义
引入右值引用的目的就是为了减少内存拷贝,优化程序性能
std::move 就是将一个左值变量强行转换为右值引用,相当于static_cast
std::move 并不会真正地移动对象,真正的移动操作是在RD自己实现的移动构造函数中
#include <string.h>
#include <iostream>
class Test
{
public:
Test()
{
buffer = new char[1024];
datasize = 1024;
}
Test(const Test& test)
{
// 调用深拷贝
printf("l copy construct\n");
this->buffer = new char[test.datasize];
memcpy(this->buffer, test.buffer, test.datasize);
this->datasize = test.datasize;
}
Test(Test&& test)
{
printf("r copy construct\n");
// 自己实现真正的移动
this->buffer = test.buffer;
this->datasize = test.datasize;
test.buffer = nullptr;
test.datasize = 0;
}
~Test()
{
if (nullptr != buffer)
{
delete[] buffer;
buffer = nullptr;
}
datasize = 0;
}
private :
char* buffer;
int datasize;
};
int main(int argc, char* argv[])
{
Test test;
// 调用普通的拷贝构造函数
Test test1(test);
// std::move 只是告诉编译器,让其调用移动构造函数,真正的移动需要RD自己实现
Test test2(std::move(test));
return 0;
}
3、完美转发std::forward
当我们用一个变量接收右值引用时,根据C++ 标准的定义,这个变量变成了一个左值。那么他永远不会调用接下来函数的右值版本,这可能在一些情况下造成拷贝。
为了解决这个问题 C++ 11引入了完美转发,支持右值判断的推导,若原来是一个右值,那么他转出来就是一个右值,否则为一个左值。
#include <string.h>
#include <iostream>
class Test
{
public:
Test()
{
buffer = new char[1024];
datasize = 1024;
}
Test(const Test& test)
{
// 调用深拷贝
printf("l copy construct\n");
this->buffer = new char[test.datasize];
memcpy(this->buffer, test.buffer, test.datasize);
this->datasize = test.datasize;
}
Test(Test&& test)
{
printf("r copy construct\n");
// 实现真正的移动
this->buffer = test.buffer;
this->datasize = test.datasize;
test.buffer = nullptr;
test.datasize = 0;
}
~Test()
{
if (nullptr != buffer)
{
delete[] buffer;
buffer = nullptr;
}
datasize = 0;
}
private :
char* buffer;
int datasize;
};
int main(int argc, char* argv[])
{
Test test;
// 只是强转为r reference,没做任何其他事
Test&& test1 = std::move(test);
// 由于**test1这个右值引用本身是左值**,此处会调用拷贝构造
Test test2(test1);
// forward支持对test1类型推导,test1为右值引用,会转成右值,调用移动构造函数
Test test3(std::forward<Test>(test1));
return 0;
}