7

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

只有在发布模式下编译时才会发生这种情况,auto_ptrs a_ 和 b_ 的类型不同,并且抛出的异常不继承自 std::exception。它似乎在 VC++ 2005 和 C++ Builder 2009 中运行良好。我确实安装了 BDS2006 更新 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
捕捉到异常


拿到:

C::C() 1
C::C() 2
C::C() 3
C::~C() 2
C::~C() 1
捕捉到异常


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

C::C() 1
C::C() 2
C::C() 3
C::~C() 1
捕捉到异常


编辑:进行了建议的更改

编辑 2:
我刚刚使用 C++ Builder 2007 (11.0.2902.10471) 对其进行了测试,它显示了同样的问题。只要我检查了项目 -> 选项 -> C++ 编译器 -> 调试中的“调试信息”框,发布配置就会起作用。令我惊讶的是,启用“调试信息”后可执行文件变得更小(从 39.5 KB 降至 31.5 KB)。

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

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

7 回答 7

5

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

于 2009-01-23T17:21:09.827 回答
4

不管它值多少钱,GCC 3.4.6 都做了预期的事情:

$ g++ main.cpp

$ a.out
C::C()
C::C()
C::~C()
C::~C()
caught exception
于 2009-01-23T17:24:05.343 回答
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
于 2009-05-05T15:50:22.213 回答
1

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

编译器无法知道构造函数是否完成得足以让析构函数正确运行。

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

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

于 2009-01-23T17:15:45.657 回答
1

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

于 2009-01-23T17:24:47.760 回答
1

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

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 类没有调用相应的析构函数!如果您有兴趣进一步诊断此错误,您可以执行的一个技巧是在 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();
}

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

于 2010-12-09T07:28:03.547 回答
0

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

于 2009-01-23T17:26:26.057 回答