# C++ 的移动语义与右值引用
# 移动语义
# 为何需要移动语义
假设有如下代码:
vector<string> vstr;// build up a vector of 20000 strings,each of 1000 characters
vector<string> vstr_copy1(vstr);
为了初始化 vstr_copy1 先调用 vector 的复制构造函数,使用 new 给 20000 个 string 对象分配内存,而每个 string 对象又调用 string 的复制构造函数,为 1000 个字符分配内存。这很浪费时间,但如果 vstr 与 vstr_copy1 都需要使用,这很难避免。
可有时候这样不太妥当,例如 vstr 不再被使用时。例如如下定义的函数:
vector<string> allcaps(const vector<string> & vs)
{
vector<string> temp;
//让temp存储vs中string的大写版本的代码
return temp;
}
并这样使用它:
vector<string> vstr;
vector<string> vstr_copy1(vstr); //#1
vector<string> vstr_copy2(allcaps(vstr)); //#2
allcaps () 创建了对象 temp,该对象管理着 20000000 个字符;语句二使用 vector 和 string 的复制构造函数创建了一个 temp 的副本,然后删除了 temp,做了大量无用功。如果编译器直接把 temp 对数据的所有权转让给 vstr_copy2,将会更好。
实际上,我们可以把实际内容保留在原来地方,而之修改记录,这就是移动语义的本质,它避免了移动原始数据,而转为修改记录。
要实现移动语义,需要采取某种方式,让编译器知道什么时候需要复制。这就是右值引用发挥作用的地方。可定义两个构造函数,其中一个是常规复制构造函数,它使用 const 左值引用作为参数,如语句 #1。另一个是移动构造函数,它使用右值引用作为参数,该引用关联到右值实参,如语句 #2 中 allcaps (vstr) 的返回值。移动构造函数可能修改其实参,这意味着右值引用参数不应是 const。
# 如何使用
直接将变量的值赋值为参数的变量值,如果变量为指针,为了防止 double free,还需将参数的指针改为 nullptr。
例如:
Useless(Useless && f) : n(f.n)
{
++ct;
pc = f.pc;
f.pc = nullptr;
f.n = 0;
}
# 强制移动
移动构造函数和移动赋值运算符使用右值,如果要让它们使用左值,该怎么办?例如,将一个左值变量赋值给一个对象,并立刻丢弃这个变量。此时可以使用头文件 utility 中声明的函数 std::move。