先理解右值引用,要理解右值引用先要回顾左值引用,要理解左值引用先回顾下右值和左值
定义:左值与右值的定义在于一个赋值等号,赋值等号左边成为左值,等号右边成为右值
左值(L-value):表示存储在计算机内存的对象,可寻址,相当于地址值
右值(R-value):代表的为真实值,可读,即数据值
左值引用,是一种数据类型,他是对一个左值的引用,同理右值引用,也是一种数据类型,他是对优质的引用
左值引用常用的场景是函数参数的传递,相当于扩展了左值的作用域到函数中
那么在来说说右值引用,普通的右值引用好理解,下面说下将亡值,C++11中即将消失的变量,如函数的返回值为将亡值,注意将亡值是即将被回收的值,是不能用左值引用的,但是可以进行右值引用,有什么用呢,原来的时候,函数返回,返回值会被拷贝到一个左值中,然后返回值被析构,使用右值引用时候,返回值将不会被析构,直接被右值引用接管,如果返回是是复杂数据类型,就能避免不必要的拷贝构造,提高效率
void fun(int &tmp)
{
std::cout << tmp << std::endl;
}
int fun1()
{
int a = 1;
return a;
}
int main(int argc, char *argv[])
{
int b = 1; // 左值
int &rb = b; // 左值引用
int &&c = 2; // 右值引用
int &d = fun1(); // 错误,不能左值引用将亡值
int &&e = fun1(); // 正确,右值引用将亡值,接管了将亡值,详单与扩展将亡值的作用域
fun(b); // 将b的范围扩展到fun函数中
return 0;
}
下面来说说移动构造,移动构造可以理解为文件的剪切操作,或者理解为二手交易,比如一个人要挂掉了,临走时,他要将自己的东西交付给其他人,之前的做法是通过拷贝构造进行拷贝,然后自己析构挂掉,但移动构造是直接将自己的东西转交给下个人,自己挂掉
原来类有四大默认构造函数、析构函数、拷贝构造函数、赋值函数(operator=),C++11又新增了两个移动构造、移动赋值。
#include <iostream>
#include <algorithm>
class MemoryBlock
{
public:
// 构造函数
explicit MemoryBlock(size_t length)
: _length(length)
, _data(new int[length])
{
std::cout << "gouzao. length = "
<< _length << "." << std::endl;
}
// 析构函数.
~MemoryBlock()
{
std::cout << "xigou. length = "
<< _length << ".";
if (_data != nullptr)
{
std::cout << " Deleting resource.";
// Delete the resource.
delete[] _data;
}
std::cout << std::endl;
}
// 拷贝构造
MemoryBlock(const MemoryBlock& other)
: _length(other._length)
, _data(new int[other._length])
{
std::cout << "kaobeigouzao. length = "
<< other._length << ". Copying resource." << std::endl;
std::copy(other._data, other._data + _length, _data);
}
// 拷贝赋值函数.
MemoryBlock& operator=(const MemoryBlock& other)
{
std::cout << "keobeifuzhi. length = "
<< other._length << ". Copying resource." << std::endl;
if (this != &other)
{
// Free the existing resource.
delete[] _data;
_length = other._length;
_data = new int[_length];
std::copy(other._data, other._data + _length, _data);
}
return *this;
}
// 移动构造
MemoryBlock(MemoryBlock&& other) noexcept
: _data(nullptr)
, _length(0)
{
std::cout << "yidonggouzao. length = "
<< other._length << ". Moving resource." << std::endl;
// Copy the data pointer and its length from the
// source object.
_data = other._data;
_length = other._length;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other._data = nullptr;
other._length = 0;
}
// 移动赋值.
MemoryBlock& operator=(MemoryBlock&& other) noexcept
{
std::cout << "yidongfuzhi. length = "
<< other._length << "." << std::endl;
if (this != &other)
{
// Free the existing resource.
delete[] _data;
// Copy the data pointer and its length from the
// source object.
_data = other._data;
_length = other._length;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other._data = nullptr;
other._length = 0;
}
return *this;
}
// Retrieves the length of the data resource.
size_t Length() const
{
return _length;
}
private:
size_t _length; // The length of the resource.
int* _data; // The resource.
};
MemoryBlock getMemoryBlock()
{
return MemoryBlock(5);
}
// 注意: 该例子需要需要关闭编译其优化选项QMAKE_CXXFLAGS += -fno-elide-constructors
int main(int argc, char *argv[])
{
MemoryBlock m(2); // 调用构造
MemoryBlock n(m); // 调用拷贝构造
MemoryBlock n1 = m; // 调用拷贝构造
MemoryBlock n2(3);
n2 = m; // 调用拷贝赋值
//MemoryBlock &tmp = getMemoryBlock(); // 错误,不能左值引用将亡值
MemoryBlock &&tmp = getMemoryBlock(); // 调用移动构造
std::cout << std::endl;
std::cout << std::endl;
std::cout << std::endl;
MemoryBlock &&tmp1(getMemoryBlock()); // 调用移动构造
MemoryBlock tmp3(10);
// 调用移动赋值
tmp3 = std::move(m); // std::move相当于强制类型转换,将左值转为右值,否则不能编译通过
int mLentth = m.Length(); // mLentth为0 因为其内容已经移动到tmp3
std::cout << "m length: " << mLentth;
std::move(n2);
mLentth = n2.Length(); // mLentth 不为0,因为虽然强转了一次,但并没有执行移动操作
return 0;
}
右值引用和移动构造是两个相互独立的概念,只不过不是移动构造中需要用到右值引用,因为要通过右值引用来进行函数重载,如拷贝构造对应移动构造,拷贝赋值对应移动赋值
|