4

可能重复:
为什么只调用了一次析构函数?

鉴于下面的代码,我无法理解 gcc 中的输出。我希望创建和销毁两个对象,但只看到一个对构造函数和析构函数的调用。这里发生了什么事?

#include <string>
#include <iostream>

struct Huge{
        Huge() { std::cout << "Constructor" << std::endl; }
        Huge(Huge const &r) { std::cout << "Copy Constructor" << std::endl; }
        ~Huge() { std::cout << "Destructor" << std::endl; }
};

Huge g() {
        std::cout << "Entering g" << std::endl;
        Huge temp;
        std::cout << "Exiting g" << std::endl;
        return temp;
}

int main(){
        Huge h2(g());
        std::cout << "Before leaving main" << std::endl;
}

此代码在 g++ (4.4) 中的输出是

输入 g

构造函数

退出 g

离开主线前

析构函数

4

2 回答 2

6

是的,这是通过命名返回值优化的复制省略。

C++ 标准允许实现省略由 return 语句产生的复制操作,即使复制构造函数有副作用。

参考:

C++03 标准:
12.8 复制类对象:

#15

当满足某些条件时,允许实现省略类对象的复制构造,即使对象的复制构造函数和/或析构函数具有副作用。在这种情况下,实现将省略的复制操作的源和目标视为简单地引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象本应被销毁的较晚时间优化.111) 在以下情况下允许省略复制操作(可以结合起来消除多个副本):

— 在具有类返回类型的函数的 return 语句中,当表达式是与函数返回类型具有相同 cv 非限定类型的非易失性自动对象的名称时,可以通过构造自动对象来省略复制操作对象直接转化为函数的返回值

— 当一个未绑定到引用(12.2)的临时类对象将被复制到具有相同 cv-unqualified 类型的类对象时,可以通过将临时对象直接构造到省略的目标中来省略复制操作复制

于 2012-01-17T06:27:30.057 回答
1

C++ 允许避免在像您这样的情况下创建和复制额外的对象。这称为命名返回值优化。关键是你肯定知道在返回之后对象temp无论如何都会消失,并且复制构造函数的语义应该是对原始对象进行完全等效的复制。

请注意,实际上这里发生了两个优化。如果不进行优化,temp首先将对象复制到 in 的返回值中g,然后将返回值复制到h2in 中main。命名的返回值优化将副本省略为返回值。因为返回值是一个临时对象,所以从返回值复制到h2被省略,在这里,复制的创建也可能被省略。

请注意,与其他优化不同,即使它们改变了可观察的行为(如在您的测试程序中),这些优化也是允许的。这是因为否则这些优化在很多情况下都无法执行,因为它不会产生影响(事实上,除了调试输出,这在编写良好的程序中永远不会产生影响),因为编译器通常无法证明省略不会改变可观察到的行为。另一方面,无法手动删除副本,因此编译器能够自动删除副本很重要。

最终发生的事情是对象temp是直接在h2占用空间中创建的,因此在返回语句处h2确实已经包含正确的值。换句话说,由于优化temph2实际上是同一个对象。

于 2012-01-17T06:30:41.240 回答