C ++:在对象范围外调用析构函数?

时间:2014-10-08 05:09:42

标签: c++ destructor

更新1:按建议添加“打印”。

更新2:拆分为多个文件,尝试阻止gcc优化。

更新3:记录的复制构造函数并输入添加函数。

更新4:在主要内容中添加了Clang和第二个cout的输出。

我希望将参数析构函数作为函数中的最后一个语句调用。从此以后,我希望以下代码输出以下内容。

default constructor: 008DFCF8
other constructor: 008DFCEC
copy constructor: 008DFBC0
in member add
destroying: 008DFBC0
copy constructor: 008DFBB8
copy constructor: 008DFBB4
in function add
destroying: 008DFBB4
destroying: 008DFBB8
3 == 3
end of main
destroying: 008DFCEC
destroying: 008DFCF8

使用MSVC(Visual Studio)时,输出符合预期。但GCC(4.8.2-19ubuntu1)输出以下内容,表明函数参数的析构函数是在main()中的第一个cout语句之后但在最后一个之前调用的。

default constructor: 0x7fff2fcea510
other constructor: 0x7fff2fcea520
copy constructor: 0x7fff2fcea550
in member add
copy constructor: 0x7fff2fcea540
copy constructor: 0x7fff2fcea530
in function add
3 == 3
destroying: 0x7fff2fcea530
destroying: 0x7fff2fcea540
destroying: 0x7fff2fcea550
end of main
destroying: 0x7fff2fcea520
destroying: 0x7fff2fcea510

对于那些很奇怪clang ++(3.4-1ubuntu3)输出的人。

default constructor: 0x7fff52cf9878
other constructor: 0x7fff52cf9870
copy constructor: 0x7fff52cf9860
copy constructor: 0x7fff52cf9858
in function add
3 == copy constructor: 0x7fff52cf9850
in member add
3
destroying: 0x7fff52cf9850
destroying: 0x7fff52cf9858
destroying: 0x7fff52cf9860
end of main
destroying: 0x7fff52cf9870
destroying: 0x7fff52cf9878

问题:

  1. 我最初的怀疑是海湾合作委员会正在强调这些职能?如果这是真的,有没有办法禁用此优化?
  2. C ++规范中的哪个部分允许在main中的cout之后调用析构函数?特别感兴趣的是关于内联的规则,如果相关,以及何时安排析构函数。

  3. // Test.h
    #ifndef __TEST_H__
    
    #include <iostream>
    
    using namespace std;
    
    class Test
    {
    public:
        int val;
    
        Test(Test const &a) : val(a.val)
        {
            cout << "copy constructor: " << this << endl;
        }
    
        Test() : val(1)
        {
            cout << "default constructor: " << this << endl;
        }
    
        Test(int val) : val(val)
        {
            cout << "other constructor: " << this << endl;
        }
    
        ~Test()
        {
            cout << "destroying: " << this << endl;
        }
    
        int add(Test b);
    };
    
    #endif
    

    // Add.cpp
    #include "Test.h"
    
    int Test::add(Test b)
    {
        cout << "in member add" << endl;
        return val + b.val;
    }
    
    int add(Test a, Test b)
    {
        cout << "in function add" << endl;
        return a.val + b.val;
    }
    

    // Main.cpp
    #include "Test.h"
    
    int add(Test a, Test b);
    
    int main()
    {
        Test one, two(2);
    
        cout << add(one, two) << " == " << one.add(two) << endl;
    
        cout << "end of main" << endl;
    
        return 0;
    }
    

    使用以下代码编制GCC:

    g++ -c Add.cpp -o Add.o ; g++ -c Main.cpp -o Main.o ; g++ Add.o Main.o -o test
    

3 个答案:

答案 0 :(得分:2)

在调用“add(a,b)”时,以及在调用成员add(b)时创建临时对象。 我认为在gcc的情况下你看到的是当这些函数返回时,add()函数(参数)中的局部变量被销毁。 最后两行“完成”行用于变量“一”和“两”。

VC是不同的 - 但这没有错,它只是表明两个编译器正在以不同的方式优化代码。

不要只打印“完成”,还要尝试打印“this”值。在构造函数中打印“this”,然后你可以看到构造函数和析构函数调用是如何配对的。

哎呀 - 我在VC和GCC之间加了一点。 VC首先打印“完成”三次 - 可能是因为add()参数被销毁,而GCC将它们全部打开,可能是因为它内联了添加函数。

答案 1 :(得分:2)

考虑这一行:

cout << add(one, two) << " == " << one.add(two) << endl;

写作:

cout << add(one, two);
cout << " == " << one.add(two) << endl;

这会改变GCC的打印输出吗?

或那样:

auto i = add(one, two);
cout << i << " == ";
auto j = one.add(two)
cout << j << endl;

我认为这是关于副作用(不是关于内联)。 VC似乎更早地调度副作用(破坏临时对象),而GCC在语句结束时安排它 - ;


Added quote:

  

临时对象生存期

     

在各种情况下创建临时对象:绑定a   引用prvalue,从函数返回prvalue,强制转换为a   prvalue,抛出异常,进入异常处理程序,然后进入   一些初始化。 在每种情况下,所有临时工作都被摧毁 as   评估全表达的最后一步(词法)   包含创建它们的点,如果是多个   临时的创造,他们按照相反的顺序被摧毁   创造的顺序。即使评估结束,也是如此   抛出异常。

在我看来,这说明GCC和VC(特别是在破坏后打印“3 == 3”,这对我来说很奇怪)。

答案 2 :(得分:2)

似乎C ++标准在确定何时必须调用函数参数析构函数时可能有点模棱两可。 C ++ 03和C ++ 11都在5.2.2 / 4“函数调用”(强调添加)中说:

  

参数的生命周期在其所在的函数结束时结束   定义的回报。每个参数的初始化和销毁   发生在调用函数的上下文中。

因此参数的析构函数在概念上不会在函数的右括号中出现。这是我不知道的事情。

该标准给出了一个说明,解释了这意味着如果参数的析构函数抛出,那么只考虑调用函数或“更高”的异常处理程序(具体来说,即使被调用函数具有'函数 - 尝试 - 阻止',它不被考虑。)

虽然我认为目的是针对MSVC的行为,但我可以看到某人如何解释允许GCC行为的阅读。

然后,也许这是海湾合作委员会的一个错误?