为什么在此代码中忽略析构函数?

时间:2009-01-23 17:10:36

标签: c++ destructor c++builder

以下代码演示了Turbo C ++ Explorer项目中的一个奇怪问题。超出范围后,D :: D()中的三个堆栈对象之一不会被销毁。

只有在发布模式下编译时才会发生这种情况,auto_ptrs a_和b_属于不同类型,抛出的异常不会从std :: exception继承。它似乎在VC ++ 2005和C ++ Builder 2009中运行得很好。我确实安装了BDS2006 Update 2,修补程序汇总和修补程序12。

是我的代码还是编译器?你知道修复吗?无法在VCL项目中可靠地使用auto_ptr会非常不方便。


#include <memory>
#include <stdexcept>
#include <iostream>

typedef std::exception my_error; // will work fine if replaced with line below
//class my_error : public std::exception {};

class A {};
class B {};

class C
{
public:
    C(int id) : id_(id) { std::cout << "C::C() " << id_ << std::endl; };
    ~C() { std::cout << "C::~C() " << id_ << std::endl; };
private:
    int id_;
};

class D
{
public:
    D()
    {
        C c1(1);
        C c2(2);
        C c3(3);

        throw my_error();
    };

private:
    std::auto_ptr<A> a_;
    std::auto_ptr<B> b_; // will work fine if replaced with line below
//  std::auto_ptr<A> b_;
//  std::auto_ptr<C> c_; // see expected output
};

#pragma argsused
int main(int argc, char* argv[])
{
    try
    {
        D d;
    }
    catch (...)
    {
        std::cout << "caught exception" << std::endl;
    }

    return 0;
}


预期:

C::C() 1
C::C() 2
C::C() 3
C::~C() 3
C::~C() 2
C::~C() 1
caught exception


得到:

C::C() 1
C::C() 2
C::C() 3
C::~C() 2
C::~C() 1
caught exception


得到(行'// std::auto_ptr<C> c_;'取消注释):

C::C() 1
C::C() 2
C::C() 3
C::~C() 1
caught exception


修改:进行建议更改

编辑2:
我刚用C ++ Builder 2007(11.0.2902.10471)测试了它,它显示了同样的问题。检查项目中的“调试信息”框时,发布配置正常工作 - &gt;选项 - &gt; C ++编译器 - &gt;调试。让我感到惊讶的是,在启用“调试信息”的情况下,可执行文件变得更小(从39.5 KB减少到31.5 KB)。

编辑3:
在Turbo C ++ Explorer(C ++ Builder 2006)(10.0.2288.42451)中,如果取消选中Project中的“内联函数扩展(-vi)”框,则发布配置有效。选项 - &gt; C ++编译器 - &gt;调试。用以下代码替换第一行(#include <memory>)也可以使用它。

#pragma option push -vi-
#include <memory>
#pragma option pop 

7 个答案:

答案 0 :(得分:5)

这似乎是一个编译器错误。我刚刚在VS2008SP1中运行了相同的样本并获得了预期的输出。

答案 1 :(得分:4)

无论它的价值如何,GCC 3.4.6都做了预期的事情:

$ g++ main.cpp

$ a.out
C::C()
C::C()
C::~C()
C::~C()
caught exception

答案 2 :(得分:2)

这是C ++ Builder 2006中的编译器错误.C ++ Builder 2009修复了它;这是我为BCC v6.1获得的输出:

C::C() 1
C::C() 2
C::C() 3
C::~C() 3
C::~C() 2
C::~C() 1
caught exception

答案 3 :(得分:1)

如果在对象构造函数中抛出异常,则析构函数将不会运行。

编译器无法知道构造函数是否已经足够完成以使析构函数正确运行。

请参阅http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.4

编辑:回应以下评论...... 在这种情况下,很可能是编译器错误将“不运行析构函数”规则与错误地不破坏堆栈中的对象混为一起。

答案 4 :(得分:1)

也许cout流没有刷新?你可以试试cerr吗?或者直接在析构函数中放置断点并检查它们是否被击中?

答案 5 :(得分:1)

我刚刚在免费命令行bcc5.5.1和C ++ Builder 6 bcc5.64上对此进行了测试,并且它们都按预期工作 - 考虑到它们的年龄,这是一个惊喜。然后我在C ++ Builder 2007,bcc5.93中尝试了这个,并且那里存在bug。事实上,示例代码可以简化为原始类型,并且仍然存在错误:

class D
{
public:
    D();

private:
    std::auto_ptr<int>      a_;
    std::auto_ptr<short>    b_;
    std::auto_ptr<char>     c_;
    std::auto_ptr<bool>     d_;
};

这个极端的例子最终导致没有为C类调用相应的析构函数!如果您对进一步诊断此bug感兴趣,可以执行的一个技巧是在D :: D()ctor中插入汇编断点:

// Note that D::D() ctor can't be inlined if it contains assembly
// limitation of borland compilers unfortunately
D::D()
{
    __asm int 3;
    C c1(1);
    C c2(2);
    C c3(3);

    throw my_error();
}

然后,您将让它通过调试器运行。当执行到达指定的断点时,程序将停止并且控制将转移回调试器。然后,您可以单步执行程序集以查看问题所在。

答案 6 :(得分:0)

看起来像异常处理堆栈展开代码中的错误。尝试在D的构造函数中创建一个简单的类E,并查看它是否被调用。