【问题标题】:Can someone explain the output of this c++ program?有人可以解释这个 c++ 程序的输出吗?
【发布时间】:2016-06-14 15:10:13
【问题描述】:

为什么输出 foo3 等于 3 ? 我建议,当调用bar(foo1) 时,函数bar 在堆栈上创建foo1 的副本,因此它的值等于0,当返回此值时,foo3 的复制构造函数递增值再次,所以它应该是 2?

提前致谢。

这是我的代码:

#include <iostream>
struct Foo {
    Foo()
        : x(0)
    {
    }
    Foo(const Foo& foo)
        : x(foo.x + 1)
    {
    }
    int x;
};

Foo bar(Foo foo)
{
    foo.x++;
    return foo;
}

int main()
{
    Foo foo1;
    Foo foo2 = foo1;
    std::cout << "A:" << foo1.x << std::endl;
    std::cout << "B:" << foo2.x << std::endl;
    Foo foo3 = bar(foo1);
    std::cout << "C:" << foo3.x << std::endl;
}

输出:

A:0
B:1
C:3

【问题讨论】:

  • 了解RVO and NRVO
  • 也许使用调试器并在复制构造函数中放置一个断点会有所帮助。
  • Foo bar(Foo&amp; foo) 这样的 bar 定义会产生输出 C:2
  • @AlokSave NRVO / RVO 在变量是函数参数时不适用。如果 bar 是:Foo bar(Foo const&amp; foo){ Foo foo2(foo); foo2.x ++ ; return foo2; },那么编译器将被允许进行复制省略,在这种情况下,现代编译器可能会给出:A:0 B:1 C:2
  • 如果他们回答了您的问题,请将其中一个答案标记为已接受。

标签: c++ rvo nrvo


【解决方案1】:

我相信这里有三个复制构造函数在起作用,foo2 = foo1 行,foo1bar 的传递,以及foo1bar 的返回。

修改你的代码可以清楚地知道发生了什么:

#include <iostream>
struct Foo {
    Foo()
        : x(0)
    {
        std::cout << "Constructor called" << std::endl;
    }
    Foo(const Foo& foo)
        : x(foo.x + 1)
    {
        std::cout << "Copy constructor called" << std::endl;
    }
    int x;
};

Foo bar(Foo foo)
{
    std::cout << "B2:" << foo.x << std::endl;
    foo.x++;
    return foo;
}

int main()
{
    Foo foo1;
    Foo foo2 = foo1;
    std::cout << "A:" << foo1.x << std::endl;
    std::cout << "B:" << foo2.x << std::endl;
    Foo foo3 = bar(foo1);
    std::cout << "C:" << foo3.x << std::endl;
}

输出:

Constructor called
Copy constructor called
A:0
B:1
Copy constructor called
B2:1
Copy constructor called
C:3

【讨论】:

    【解决方案2】:
    • 0 -&gt; 1你把foo1复制到bar的参数中。
    • 1 -&gt; 2bar 内增加x
    • 2 -&gt; 3你把bar复制到临时返回值。

    我数了三个增量。

    【讨论】:

      【解决方案3】:

      做的时候:

      Foo foo2 = foo1;
      

      您正在调用复制 ctor,因此 foo2.x 的值为 1。 那么调用时:

      Foo foo3 = bar(foo1);
      

      因为您没有通过引用传递,所以“bar”的参数是使用复制 ctor (x == 1) 将 foo1 的副本创建到 bar 中。之后,该副本的 x 成员递增(x == 2)并最终返回。返回时不会创建另一个副本,因为“返回值优化”(仍然 x == 2)。 最后 foo3 再次使用复制 ctor(x == 3) 初始化。

      【讨论】:

        【解决方案4】:

        当您创建第一个foo1 时,其x 为零。对bar 的调用按值传递,因此它被复制 - 因此bar 中的参数值为1。函数bar 本身将x 进一步增加1,因此现在为2。最后, return 语句再次按值返回,因此还有另一个副本 - 因此最终的 x 是 3。

        注意 RVO 不适用于此处,因为 Foo 不是按值返回局部变量,而是返回一个参数,该参数实际上必须被复制。

        根据标准[class.copy/31],允许复制省略的情况,特别排除了返回函数参数的情况:

        ——在具有类返回类型的函数的返回语句中,当 表达式是非易失性自动对象的名称(other 比具有相同 cv-unqualified 的函数或 catch 子句参数) type 作为函数返回类型,可以进行复制/移动操作 通过将自动对象直接构造到 函数的返回值

        【讨论】:

        • 对于 RVO,(按值)参数和局部变量有什么区别?
        • @sepp2k:调用约定规定了必须放置参数和返回值的位置,无法灵活地使这些重叠。它不规定临时 (RVO) 或局部变量 (NRVO) 的放置位置,让编译器可以灵活地将其直接放置在返回值位置。
        • @BenVoigt 这是有道理的。谢谢。
        • @sepp2k - 根据上述编辑标准,在返回函数参数时明确禁止复制省略。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-07-22
        • 2020-07-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多