为什么类移动构造函数的参数是右值引用
1. 右值引用 &&
移动构造函数的标准形式:
class MyClass {
public:
// 移动构造函数 (Move Constructor)
MyClass(MyClass&& other) noexcept {
// ... 实现移动逻辑 ...
}
// ... 其他成员函数 ...
};
这里的 MyClass&& 读作“MyClass 的右值引用”。这个 && 是一个单独的符号,代表右值引用,而不是两个 & (引用) 的组合。
2. 为什么要用右值引用 &&?
要理解为什么需要一种新的引用类型,需要先了解 C++ 中的左值 (lvalue) 和右值 (rvalue)。
-
左值 (lvalue - locator value):可以简单理解为“有名字、有固定内存地址”的对象。你可以对它取地址。比如变量、函数返回的引用等。
int x = 10; // x 是一个左值 std::string s = "hi"; // s 是一个左值 -
右值 (rvalue - read value):可以简单理解为“临时的、即将被销毁”的值。你通常不能对它取地址。比如字面量、函数返回的非引用临时对象等。
// 以下都是右值 10 true std::string("hello") get_string() // 假设 get_string() 返回一个 string 对象
C++11 之前的问题
在 C++11 之前,我们只有一种引用类型:左值引用 (&)。它有一个限制:
- 非
const的左值引用 (&) 只能绑定到左值。
std::string s = "hello";
std::string& ref1 = s; // 正确:左值引用绑定到左值
// std::string& ref2 = std::string("world"); // 错误!不能将非const左值引用绑定到右值
这意味着,我们无法在函数参数中区分一个对象是“持久的左值”还是“临时的右值”。
唯一的例外是 const 左值引用,它可以同时绑定到左值和右值。
const std::string& ref3 = s; // 正确
const std::string& ref4 = std::string("world"); // 正确
这正是拷贝构造函数 MyClass(const MyClass& other) 使用 const & 的原因,这样它既能从左值拷贝,也能从右值拷贝。
但问题来了:当我们从一个右值(临时对象)进行构造时,我们知道这个临时对象马上就要被销毁了。对它进行一次完整的深拷贝(比如复制堆上的内存)是巨大的浪费。如果能直接“偷”走它的资源(比如指针),效率会高得多。这就是“移动”的本质。
然而,const MyClass& 是常量引用,我们不能修改它引用的对象,所以我们无法“偷”走它的资源(例如,不能将它的指针设为 nullptr)。
C++11 的解决方案:右值引用 &&
为了解决这个问题,C++11 引入了右值引用 (&&)。
- 右值引用 (
&&) 专门用于绑定到右值。
这就像给编译器一个明确的信号:“嘿,我正在处理一个临时对象,你可以对它为所欲为,因为它马上就要消失了。”
这使得函数重载成为可能:
class SmartString {
private:
char* data;
public:
// 1. 拷贝构造函数 (处理左值)
SmartString(const SmartString& other) {
std::cout << "调用拷贝构造函数 (深拷贝)\n";
// ... 执行昂贵的深拷贝 ...
}
// 2. 移动构造函数 (处理右值)
SmartString(SmartString&& other) noexcept {
std::cout << "调用移动构造函数 (资源窃取)\n";
// 直接“偷”走 other 的资源
this->data = other.data;
// 把 other 的指针设为 null,防止它在析构时释放我们已经“偷”来的资源
other.data = nullptr;
}
// ...
};
SmartString create_string() {
return SmartString("temp");
}
现在,编译器可以根据传入的参数类型,智能地选择最优的构造函数:
SmartString s1("persistent"); // s1 是一个左值
// 场景一:参数是左值
SmartString s2 = s1; // s1 是左值,编译器选择拷贝构造函数
// 场景二:参数是右值
SmartString s3 = create_string(); // create_string() 返回一个临时对象(右值)
// 编译器选择移动构造函数
SmartString s4 = std::move(s1); // std::move 将 s1 强制转换为右值
// 编译器选择移动构造函数
总结
&&是 C++11 引入的一种新的引用类型,专门用于绑定到右值(临时对象)。- 引入
&&的核心目的是为了实现移动语义 (Move Semantics)。它允许我们重载函数(如构造函数),为右值提供一个特殊的、更高效的版本。 - 移动构造函数通过“窃取”临时对象的内部资源(如指针),避免了不必要的深拷贝,从而极大地提高了性能,尤其是在处理大型对象(如容器、字符串等)时。
所以,下次看到 MyClass&&,请记住它的名字是“右值引用”,它的使命就是识别出那些即将逝去的临时对象,并榨干它们的“剩余价值”。

浙公网安备 33010602011771号