以下代码演示了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
答案 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,并查看它是否被调用。