右值引用
左值:可以被取地址的变量或值
(相关资料图)
右值:无法被修改,无法取地址的值。一般为临时变量。
左值引用:
常引用,只能指向左值
或者通过
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()
就起到这个作用:std::move(var) --- 作用是类型转换:接受一个左值作为参数,返回其右值引用
所以此时使用
Obj b(std::move(a))
,就用移动构造初始化了对象b
很多类的成员函数实际上都实现了这两种方法,比如vector的push_back():
// 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被置为空,不能再被使用
标签: