gcc C++析构函数运行两次

utugiqy6  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(236)

请看下面的程序。程序应该打印1,因为计数器对象1仍然存在,但是当用GCC编译时,它打印0。为什么会这样?这是编译器错误吗?这只发生在从不同的作用域返回时。删除if完全修复了所有编译器上的错误。

#include <iostream>

int counter = 0;

class Counter {
public:
    Counter() {
        counter++;
    }
    ~Counter() {
        counter--;
    }
};

Counter test() {
    if (true) { // REMOVING THIS FIXES IT
        Counter c;
        return c;
    } else {
        throw std::logic_error("Impossible!");
    }
}

int main() {
    Counter c = test();
    std::cout << counter << std::endl; // 0 on GCC (incorrect), 1 on clang (correct)
    return 0;
}
wbgh16ku

wbgh16ku1#

在C++中,从一个函数返回一个对象时,在调用方的上下文中复制构造该对象,然后在返回的函数中销毁复制自的对象。在某些情况下,这种复制可以省略。

Counter c;
       return c;

这被称为返回值优化,在这种情况下它不是强制的。这里的副本省略是允许的,但它是可选的。你使用的编译器之一省略了这个副本,另一个没有。
如果没有复制省略,编译器会在调用方的上下文中复制构造返回的对象。
这个Counter缺少复制构造函数,因此显示的代码无法记录复制构造对象的示例。
只需添加一个复制构造函数:

class Counter {
public:
    Counter() {
        counter++;
    }
    Counter(const Counter &) {
        counter++;
    }
    ~Counter() {
        counter--;
    }
};

现在,无论是否使用副本省略,您都将获得预期的结果。
如果你在复制构造函数中设置了断点,你会在从函数返回时看到断点命中(当使用的编译器不取消复制时)。

qqrboqgw

qqrboqgw2#

destructor会在中有对象的范围结尾呼叫。
1.当第一行--在main--被激励时,它将调用test()
1.当您将条件设为true时,它会建立一个对象,这个对象会将counter加1。
1.它将是return c,但另一方面,它是作用域的结尾,这意味着将调用destructor,因此是counter--
老实说,我对我的回答也没有把握,但我认为那是什么。

相关问题