了解复制构造函数调用和命名 return 值优化

understanding copy constructor calls and named return value optimization

我一直在研究 this 关于 NRVO 的文章。

  class RVO
  {
    public:
    RVO(){
          printf("I am in constructor\n"); }
    RVO(const RVO& c_RVO) { 
          printf("I am in copy constructor\n"); }
    ~RVO(){
          printf("I am in destructor\n"); }
    int mem_var;
  };
  RVO MyMethod(int i)
  {
     RVO rvo;
     rvo.mem_var = i;
     return (rvo);
  }
  int main()
  {
        RVO rvo;
        rvo=MyMethod(5);
  }

visual studio 上的输出如下,我是这样理解的

 I am in constructor       // main rvo construction
 I am in constructor       //MyMethod rvo construction 
 I am in copy constructor  //temporary created inside MyMethod
 I am in destructor        //Destroying rvo in MyMethod
 I am in destructor        //Destroying temporary in MyMethod
 I am in destructor        //Destroying rvo of main

如果我把主要写成

 int main()
 { 
    RVO rvo = MyMethod(5);
    return 0;
 }

输出如下,如何理解

 I am in constructor       //MyMethod rvo construction 
 I am in copy constructor  //temporary created inside MyMethod
 I am in destructor        //Destroying rvo in MyMethod
 I am in destructor        //Destroying rvo of main

为什么第二个版本Mymethod中的temporary没有被销毁?

为什么在RVO rvo = MyMethod(5);中不调用复制构造函数。我认为在第二个版本中应该调用复制构造函数两次,一次用于在Mymethod中创建的临时对象,另一次用于RVO rvo = MyMethod(5); 我知道有些电话可能会接到 elided.Can 有人请帮助解释这些电话。

编辑: 使用 return rvo 而不是 return (rvo) 将输出更改为

第一个案例

 I am in constructor
 I am in constructor
 I am in destructor
 I am in destructor

第二种情况

 I am in constructor
 I am in destructor       

我想当我删除括号时,NRVO 就会启动 in.But 我对没有优化的第一个输出更感兴趣

第一次析构函数调用来自 main 中的 rvo 的析构。首先创建的对象必须被删除。完成的不是复制赋值,而是复制构造。

UPDATE:使用return rvo而不是return (rvo);

寻址更新程序的输出
I am in constructor
I am in constructor
I am in destructor
I am in destructor

你看到这个的原因是两个对象(MyMethod::rvomain::rvo)都进行了默认构造,然后后者被分配为一个单独的操作,但你没有记录它。


您可以通过输出对象的地址和作为函数调用的 this 指针值来更好地了解正在发生的事情:

#include <cstdio>
#include <iostream>
class RVO
  {
    public:
    RVO(){
          printf("%p constructor\n", this); }
    RVO(const RVO& c_RVO) {
          printf("%p copy constructor, rhs %p\n", this, &c_RVO); }
    ~RVO(){
          printf("%p destructor\n", this); }
    int mem_var;
  };
  RVO MyMethod(int i)
  {
     RVO rvo;
     std::cout << "MyMethod::rvo @ " << &rvo << '\n';
     rvo.mem_var = i;
     return (rvo);
  }
  int main()
  {
        RVO rvo=MyMethod(5);
        std::cout << "main::rvo @ " << &rvo << '\n';
  }

输出还取决于您是否使用优化进行编译;您 link Microsoft 文档,所以您可能正在使用 Microsoft 编译器 - 尝试 cl /O2.

Why is temporary not destroyed in Mymethod in the second version?

那里没有临时对象 - main 中的对象是直接复制构造的。引导您完成它:

002AFA4C constructor
MyMethod::rvo @ 002AFA4C   // MyMethod::rvo's constructed

002AFA70 copy constructor, rhs 002AFA4C   // above is copied to 2AFA70
002AFA4C destructor        // MyMethod::rvo's destructed
main::rvo @ 002AFA70       // turns out the copy above was directly to main::rvo
002AFA70 destructor        // main::rvo's destruction

[Alf's comment below] "directly copy-constructed" is not entirely meaningful to me. I think the OP means the rvo local variable

考虑程序第一个版本的增强输出(没有优化):

002FF890 constructor  // we find out this is main::rvo below
002FF864 constructor  // this one's MyMethod::rvo
MyMethod::rvo @ 002FF864
002FF888 copy constructor, rhs 002FF864  // 2FF888 is some temporary
002FF864 destructor   // there goes MyMethod::rvo
002FF888 destructor   // there goes the temporary
main::rvo @ 002FF890
002FF890 destructor   // and finally main::rvo

如果我们将其与 OP 的输出和注释联系起来...

I am in constructor       // main rvo construction
I am in constructor       //MyMethod rvo construction 
I am in copy constructor  //temporary created inside MyMethod
I am in destructor        //Destroying rvo in MyMethod
I am in destructor        //Destroying temporary in MyMethod
I am in destructor        //Destroying rvo of main

OP(正确地)将复制构造的对象称为临时对象。当我说程序的第二个版本时 "There was no temporary there - the object in main was directly copy-constructed." - 我的意思是没有与我们在上面直接分析的第一个程序中的临时等效项,而是 main::rvo 从 [=17 复制构造的=].