0

我来自 Java,任何东西都可以参考,所以我试图弄清楚 C++ 实例创建的基础。

Employee getEmp(int a) {
    Employee local(a);
    return local;
}
Employee myEmp = m.getEmp(10);

有 3 次 Employee 实例化(没有 RVO)是否正确?
1 - 创建本地副本
2 - 创建临时副本以绑定到引用)
3 - 创建 myEmp

为什么需要在第 2 步?为什么不直接将本地复制到myEmp?

如果我打算只使用 Ubuntu,我应该依靠 RVO,我是否更正了 RVO 只完成了两个实例创建(步骤 2 中不需要)?

谢谢!!!

4

2 回答 2

3

没有RVO/NRVOandcopy elision则有 1 次调用Employee(int)和 2 次调用copy c-tor,一次返回时调用,一次构造时调用myEmp。和- 一次调用RVO,可能是零次调用来复制 c-tor。copy elisionEmployee(int)

复制构造函数的第二次调用copy elision是强制性的,因为您想从另一个对象构造新对象(工作到copy c-tor)。

于 2012-10-02T08:39:44.820 回答
2

函数返回的语义是独立于你对返回值所做的定义的:返回的值被复制到某个地方,当本地堆栈帧被破坏时它不会消失。因此,在您的示例中,如果没有 RVO,您将拥有:

  1. local在 的堆栈帧中构造一个名为 的局部变量getEmp

  2. 在调用者可以访问的某个地方复制返回对象的构造。通常,调用者在自己的堆栈帧中为返回的对象分配内存,并将带有该内存地址的隐藏参数传递给被调用函数。RVO 或 NRVO 允许编译器将此返回的对象“别名”为本地对象:在您的情况下,local在作为隐藏参数传入的地址处创建变量,而不是在本地堆栈帧中。

  3. 返回后,返回的对象将被“使用”,然后在完整表达式的末尾销毁。 如果此用途是使用复制构造函数初始化局部变量,则可以省略中间值;编译器会将要构造的实际对象的地址作为隐藏参数传递给被调用的函数。(严格来说,这不是 NRVO,但它是相关的。)但是,不能省略其他用途;例如,如果返回值用于对现有变量的赋值,则赋值运算符将需要对要赋值的实例的引用。对中间对象的正式要求是存在的,因为返回值的大多数用途不是作为复制构造函数的参数。

最后,鉴于现代编译器技术,您可能可以指望编译器系统地省略上面的第二个副本。第一个副本(RVO 或 NRVO)可能更难一些。我希望在像上面这样的简单情况下系统地看到它:RVO 任何时候都有一个临时返回,而 NRVO 任何时候都有一个返回,它返回在函数的最外层范围内定义的命名变量。如果函数中有多个返回,则 NRVO(实际上也是 RVO,尽管我不知道为什么)不太可能发生。

于 2012-10-02T09:39:52.157 回答