【问题标题】:C++ move constructor and scopeC++ 移动构造函数和作用域
【发布时间】:2014-03-22 19:57:45
【问题描述】:

如果我在堆栈上声明了一个对象,并且我返回了对它的引用,我相信我将无法再访问它,因为它超出了范围。对吗?

如果我只返回对象本身(而不是对它的引用)怎么办?会调用复制构造函数吗? (我听说过“移动构造函数”这个词,但从我的阅读来看,它似乎是一个新功能。有人可以详细说明一下吗?)

在什么情况下会调用析构函数?

【问题讨论】:

    标签: c++ scope copy-constructor move-constructor


    【解决方案1】:

    如果我在堆栈上声明了一个对象,并且我返回了对它的引用,我相信我将无法再访问它,因为它超出了范围。对吗?

    正确,析构函数超出范围时会被调用。

    如果我只返回对象本身(而不是对它的引用)怎么办?会调用复制构造函数吗?

    可以调用复制构造函数和析构函数,但通常编译器会执行返回值优化,不会进行复制、析构函数或移动。

    要了解移动构造函数,请阅读移动语义和右值引用。

    【讨论】:

    • 所以我不能保证第二种情况下的行为?标准是否对此有任何规定?
    • @Radu 这确实不能由符合标准的编译器保证。然而,大多数现代编译器都会尽可能地省略副本,如下所述:en.cppreference.com/w/cpp/language/copy_elision
    • @Radu 复制构造函数和析构函数应该没有“副作用”,因此在大多数情况下,您不需要任何关于编译器选择做什么的保证。
    【解决方案2】:

    如果我在堆栈上声明了一个对象,并且我返回了对它的引用,我相信我将无法再访问它,因为它超出了范围。对吗?

    正确,许多编译器也会给你警告。

    如果我只返回对象本身(而不是对它的引用)怎么办?会调用复制构造函数吗?

    它会调用复制构造函数。标准的 std::array 没有 std::move 构造函数在内部定义为它使用堆栈内存。 因此将调用复制构造函数。

    在什么情况下会调用析构函数?

    在这两种情况下都会调用析构函数。只是在移动构造函数之后,我们将所有指针/句柄移动到其他对象,因此对象状态将是一种空(但一致)。

    【讨论】:

    • 如果按值返回,它不必调用任何构造函数甚至析构函数,这就是复制省略和 RTVO 的用途。
    • 我从对象的角度解释了更多,因为它在原始问题中被问到。RTVO 确实依赖于编译器,但是如果您定义了移动构造函数,那么标准保证它会被调用在这种情况下。
    • 不,移动也可以省略。引用标准 §12.8.31 “当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数具有副作用。”
    【解决方案3】:

    编译器将尝试优化此类函数以消除复制操作。生成的代码将表现得好像对象是在函数以适当的生命周期返回它的点创建的。参考here。 这取决于编译器,以及代码本身在构造和使用点的结构。也可能无法进行优化,在这种情况下,您可能需要调用复制或移动构造函数(如果可用)。

    如果编译器对函数进行了优化,那么当使用点的对象超出最近的块范围时,将调用析构函数。

    如果没有优化,那么对象会在使用点被复制到另一个(一次调用复制/移动构造函数),然后函数中的对象将被销毁(如果被复制),最后是析构函数复制/移动的对象将在函数返回它的位置(使用点)离开最近的块范围时被调用。

    但是,如果您从函数返回一个临时对象,那么您将返回一个指向已破坏内存的引用或指针,因为您的临时对象将在函数返回时超出范围并被销毁。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-23
      • 2018-01-23
      • 2012-05-11
      • 2021-05-31
      • 1970-01-01
      • 2011-05-22
      • 1970-01-01
      • 2016-12-16
      相关资源
      最近更新 更多