左值:可以被取地址的变量或值
右值:无法被修改,无法取地址的值。一般为临时变量。
(资料图片仅供参考)
左值引用:
常引用,只能指向左值
或者通过 const
的方式指向一个右值
const int & a = 17;
所以,函数形参定义为const type & var
时,既可以接受变量,也可以接受右值。如std::vector
的push_back()
:
void push_back(const type & val);
如果没有const
,那v.push_back(17)
这样的代码就无法编译通过了。
右值引用: 只能指向右值的引用,指向左值则无法通过编译
使用右值引用,本质上将一个右值变成了一个左值:int &&a = 17;
变量a
是一个左值,所以右值引用是一个左值
对于函数形参而言,定义为type && var
将只接受为右值的实参。
// 类Obj数据的定义class Obj{ int _size; char *_buf;};
当我们定义Obj
类的构造函数时,如果我们想用对象a
来初始化对象b
,有时候初始化后我们还想要继续使用对象a
,那这时候的构造函数就需要将对象a
的数据完全复制给对象b
:
Obj(const Obj &o){ _size = o._size; _buf = new char[_size]; for(int i=0;i<_size;i++) _buf[i] = o._buf[i];}
而有的时候对象a
只是一个临时变量(右值),用完即弃。这时候直接将对象a
的数据移动给对象b
即可:
Obj(const Obj &o){ _size = o._size; _buf = o._buf; o._buf = nullptr;}
o._buf = nullptr
。之所以要将被移动数据的对象中的指针/引用置为空指针,原因是避免在对象a
和对象b
的析构函数中都对该地址进行delete
)但实际上,以上的移动构造函数是无法实现的,因为const
使得传入对象无法被修改。因此,为了实现移动构造函数,C++11引入了右值引用:
Obj(Obj && o){ _size = o._size; _buf = o._buf; o._buf = nullptr;}
这样问题就得到解决了。当构造函数时传入参数为右值时,会调用Obj(Obj && o)
;若是左值,则会调用Obj(const Obj & o)
。
此时,又有另外一个问题,如果使用一个对象a
是一个对象而非一个临时变量,但我依旧想要使用移动数据的方式来构造函数(对象a
在之后不再使用),那该怎么办呢?
如果依旧使用Obj b(a)
来构造,那还是会复制构造。要使用移动构造,就得使用某种方法将对象a
变成右值。而函数std::move()
就起到这个作用:
所以此时使用 Obj b(std::move(a))
,就用移动构造初始化了对象b
// std::vector方法定义void push_back(const type & value);void push_back(type && value);vector vs;string str = "hello world";vs.push_back(str); // 此时是传入左值,push_back会深拷贝strvs.push_back(std::move(str)); // 此时传入了右值,push_back浅拷贝str作为vs中的元素 // 同时str被置为空,不能再被使用
关键词: