cod*_*oet 2 c++ stack-frame return-value-optimization copy-elision
假设有一个用户定义的class Foo。一些帖子表明 C++ 类对象“永远”不会在堆上分配,除非使用new. 但!另一方面,有一些帖子建议从函数中按值返回本地外观的类对象不一定会复制任何数据。所以!这样一个对象的数据首先存储在哪里?还在堆吗?它是否被提升到调用函数的堆栈帧?
class Foo {
...
}
Foo a(int x) {
Foo result;
doabc(x, result);
return result;
}
Foo b(int x) {
Foo result = a(x);
doxyz(x,result);
return result;
}
int main() {
int x;
cin >> x;
Foo result = b(x);
dosomethingelse(result);
cout << result;
}
Run Code Online (Sandbox Code Playgroud)
如果 a 的 Foo 结果不是按值复制的,那么它在哪里分配?堆还是栈?如果在堆上,编译器是否会自动重构代码以插入删除?如果在堆栈上,它会驻留在哪个堆栈帧上?b 的?这是让我想知道的帖子:/sf/answers/1223171211/。谢谢!
C++ 语言中没有堆或栈。这些是实现的属性,而不是语言的属性。只要生成的程序执行标准规定的操作,实现就可以自由地执行任何操作。这包括在堆上为自动变量或临时变量分配存储空间。
\n但“正常”实现不会这样做。自动变量和临时变量都在堆栈或寄存器中分配。
\n在您的示例中,所有名为result具有自动存储持续时间,这通常意味着它们是在堆栈上分配的(因为实现具有诸如“堆栈”\xe2\x80\x94之类的东西,所以语言对此保持沉默)。从一个变量复制到另一个变量通常不涉及需要在某处分配的任何第三个变量或临时变量。这包括从函数返回值的情况。
对象可能拥有诸如内存之类的资源,需要在空闲存储上分配这些资源(就具有堆的实现而言,“堆”)。如果是这种情况,则复制构造函数或复制赋值运算符负责复制,并在需要的地方分配内容。但这是关于对象拥有的资源,而不是对象本身。
\n您链接的问题是关于避免复制,特别是避免复制拥有的资源。这与上述任何一点都没有什么关系。
\n允许(有时甚至需要)实现删除副本。复制省略是避免复制的一种方法。这是一个例子:
\n#include <iostream>\n\nstruct Foo\n{\n Foo() {}\n Foo(const Foo&) { std::cout << "Copied by ctor\\n"; };\n Foo& operator=(const Foo&) { std::cout << "Copied by assignment\\n"; return *this; };\n};\n\nFoo func()\n{\n Foo foo;\n return foo;\n}\n\nint main()\n{\n Foo foo (func());\n}\nRun Code Online (Sandbox Code Playgroud)\n现场演示。此示例显示了强制和可选的复制省略。
\n在 C++14 及以下版本中,有两种可选复制省略的情况。在 C++17 及更高版本中,存在一种可选情况和一种强制复制省略情况(C++17 中的新功能,正式称为“临时物化”)。GCC 默认执行可选的复制省略,但可以使用-fno-elide-constructors. 这是标准允许的优化。无法禁用强制复制省略,因为标准强制要求这样做。
避免复制的另一种方法是尽可能使用移动而不是复制。移动语义是一个单独的故事,超出了本答案的范围。
\n