我正在阅读 Stroustrup 编写的《编程:C++ 原理与实践》第 4 版,我对一些旨在说明复制构造函数/复制赋值/移动构造函数/移动赋值/析构函数语言特性的代码的输出感到困惑。以下代码大部分摘自本书,并实现了一个简单的类型,X
以便上述运算符(以及构造函数)在调用时输出显式信息。
#include <iostream>
#include <string>
#include <vector>
using std::cout;
using std::string;
using std::vector;
struct X {
int val;
void out(const string& s, int nv) {
cout << this << "->" << s << ":" << val << "(" << nv << ")\n";
}
X() { out("X()",0); val=0; }
explicit X(int x) { out("X(int)",x); val=x; }
X(const X& x) { out("X(X&)", x.val); val=x.val; }
X& operator=(const X& x) {
out("X copy assignment", 0);
val=x.val;
return *this; }
X(X&& x) {
out("X(X&&)",x.val);
val = x.val;
x.val = 0;
}
X& operator=(X&& x) {
out("X move assignment", x.val);
val = x.val;
x.val = 0;
return *this;
}
~X() { out("~X()", 0); }
};
X copy(X a) {
cout << "copy()\n";
return a;
}
int main() {
X loc {4};
X loc2 {12};
loc2 = copy(loc);
cout << "Done\n";
}
我运行该程序时看到的输出如下:
0x16d1370bc->X(int):1(4)
0x16d1370b8->X(int):-1834548312(12)
0x16d1370b0->X(X&):1829990608(4)
copy()
0x16d1370b4->X(X&&):1(4)
0x16d1370b8->X move assignment:12(4)
0x16d1370b4->~X():0(0)
0x16d1370b0->~X():0(0)
Done
0x16d1370b8->~X():4(0)
0x16d1370bc->~X():4(0)
我理解此输出的前四行——它们来自两个局部变量loc
和的构造函数loc2
、来自的按值调用的复制构造函数copy
以及来自函数体的打印copy
。我对下一行感到困惑——即对移动构造函数的调用。我原本以为只是看到对移动赋值运算符的调用。为什么这里调用移动构造函数(我不认为有任何对象正在构造?)?它在什么情况下被调用?特别是,我没有看到具有由移动构造函数打印的内存地址的对象构造,所以我很困惑该对象来自哪里。
感谢您的帮助,很抱歉我是 C++ 新手。如果相关的话,我正在用编译此程序g++ -Wall -std=c++20
。输出一直保留不同的编译优化级别。
正如您的输出所示,该表达式
loc2 = copy(loc)
涉及复制、移动和分配:loc
用于复制构造a
的参数copy
a
中的本地对象copy
用于移动构造copy
的返回值。copy
作为参数传递x
给X
的移动赋值运算符如果我不得不猜测,您不会将
copy
的返回值视为一个不同的对象。在许多情况下,这是准确的,因为复制省略将省略该特定副本。如果您返回一个 prvalue(即return X{42};
)并使用 C++17 或更高版本,则该优化是强制性的,但在返回命名对象时它是可选的,因为在 100% 的情况下这是不可能的。