【问题标题】:C++ Copy constructor, temporaries and copy semanticsC++ 复制构造函数、临时对象和复制语义
【发布时间】:2011-01-20 08:40:55
【问题描述】:

对于这个程序

#include <iostream>
using std::cout;

struct C 
{
    C() { cout << "Default C called!\n"; }
    C(const C &rhs) { cout << "CC called!\n"; }
};

const C f()
{
    cout << "Entered f()!\n";
    return C();
}

int main()
{
    C a = f();
    C b = a;

    return 0;
}

我得到的输出是:

Entered f()!
Default C called!
CC called!

由于f() 按值返回,它应该返回一个临时值。既然T a = x;就是T a(x);,那它不会调用拷贝构造函数来构造a,以临时传入的作为参数吗?

【问题讨论】:

    标签: c++ variable-assignment copy-constructor compiler-optimization temporaries


    【解决方案1】:

    由于f() 按值返回,它应该返回一个临时值。既然T a = x;就是T a(x);,那它不会调用复制构造函数来构造a,以临时传入的参数为参数吗?

    查找返回值优化。这是默认开启的。如果您在 Windows 上使用 MSVC 2005+,您可以使用 /Od 将其关闭并获得所需的结果(或在 GCC 上使用 -fno-elide-constructors)。另外,对于 MSVC,请参阅 this 文章。

    12.8 复制类对象

    15 当满足某些条件时, 实现允许省略 类对象的复制构造, 即使复制构造函数和/或 对象的析构函数有边 效果。在这种情况下, 实施处理源和 省略复制操作的目标 只是两种不同的方式 指的是同一个对象,并且 该对象的破坏发生在 两者的后者 对象会有 没有被摧毁 优化.115 这种复制的省略 操作是允许的 以下情况(可能是 结合消除多个 副本):

    ——在一个return语句中 具有类返回类型的函数, 当表达式是 a 的名称时 非易失性自动对象与 与 cv 非限定类型相同 函数返回类型,副本 操作可以省略 构造自动对象 直接进入函数的返回 价值 — 在 throw 表达式中,当 操作数是一个名称 非易失性自动对象, 将操作从操作数复制到 异常对象(15.1)可以省略 通过构造自动对象 直接进入异常对象

    —— 当一个临时类对象具有 未绑定到参考 (12.2) 将被复制到一个类对象 相同的 cv-unqualified 类型,副本 操作可以省略 构造临时对象 直接进入目标 省略复制

    ——当 异常声明异常 handler(第 15 条)声明一个对象 同类型的(除了 cv-qualification) 作为例外 对象(15.1),复制操作可以 通过处理被省略 异常声明作为别名 异常对象 if 的含义 该程序将保持不变,除了 用于执行构造函数和 声明的对象的析构函数 异常声明。

    注意:强调我的

    【讨论】:

    • 我使用 GCC,而 -fno-elide-constructors 它准确地显示了幕后发生的事情!有时这些优化会让学习者很困惑 :) 但是,我同意默认情况下它们应该是 ON,因为默认情况下会优化不知情的人的构建。
    • @legends2k:RVO 太有用了,不能任由用户随心所欲。此外,这是标准允许优化的极少数情况之一。这重新激发了它被保留的原因。但是,请注意,其他优化通常并非如此。
    【解决方案2】:

    这是您的编译器支持的返回值优化(RVO)功能示例。

    按值返回时可能不会调用复制构造函数。

    使用 GCC 上的 -fno-elide-constructors 选项关闭该功能。

    【讨论】:

      【解决方案3】:

      我相信它叫return value optimization

      我假设当f() 返回C 对象时,该对象被分配在调用方法的堆栈空间中,因此初始化C a 不需要副本。这是你的default C called

      C b = a
      

      这会导致一个复制构造函数,因此你的CC called

      顺便说一句,wiki 上的示例看起来与您的代码非常相似。

      【讨论】:

      • +1 表示 wiki 链接。哎呀!即使命名看起来相似,但我发誓我没有在阅读后发布,我正在阅读 Thinking in C++ :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-05
      • 2010-12-22
      • 2016-10-10
      • 2021-07-07
      • 1970-01-01
      • 2013-05-20
      • 2013-11-05
      相关资源
      最近更新 更多