質問

次のコードは、Turbo C ++ Explorerプロジェクトでの奇妙な問題を示しています。 D :: D()の3つのスタックオブジェクトの1つは、スコープ外に出ても破棄されません。

これは、リリースモードでコンパイルされ、auto_ptrs a_とb_が異なるタイプであり、スローされる例外がstd :: exceptionを継承しない場合にのみ発生します。 VC ++ 2005およびC ++ Builder 2009では正常に動作するようです。BDS2006Update 2、Hotfix Rollup、Hotfix 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)でテストしました。リリース構成は、<!> quot;デバッグ情報<!> quot;をチェックするとすぐに機能します。プロジェクトのボックス-<!> gt;オプション-<!> gt; C ++コンパイラ-<!> gt;デバッグ。 <!> quot;デバッグ情報<!> quot;で実行可能ファイルが小さくなるのは驚きです。有効(39.5 KBから31.5 KBまで)。

編集3:
Turbo C ++ Explorer(C ++ Builder 2006)(10.0.2288.42451)では、<!> quot;インライン関数展開(-vi)<!> quotをオフにするとリリース構成が機能します。プロジェクトのボックス-<!> gt;オプション-<!> gt; C ++コンパイラ-<!> gt;デバッグ。最初の行(#include <memory>)を次のコードに置き換えると、それも機能します。

#pragma option push -vi-
#include <memory>
#pragma option pop 
役に立ちましたか?

解決

これはコンパイラのバグのようです。 VS2008SP1で同じサンプルを実行したところ、期待どおりの出力が得られました。

他のヒント

価値があるものは何でも、GCC 3.4.6は期待されることを行います:

$ g++ main.cpp

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

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

オブジェクトコンストラクターで例外がスローされた場合、デストラクターは実行されません。

コンパイラには、コンストラクタがデストラクタを正常に実行するのに十分に完了したかどうかを知る方法がありません。

http://www.parashift.com/をご覧ください。 c ++-faq-lite / exceptions.html#faq-17.4

編集:以下のコメントへの応答... この場合、おそらく「デストラクタを実行しない」ルールが、スタック上のオブジェクトを誤って破棄せずにまとめているコンパイラのバグです。

coutストリームがフラッシュされていない可能性がありますか?代わりにcerrを試すことはできますか?または、デストラクタにブレークポイントを直接配置して、ヒットするかどうかを確認しますか?

無料のコマンドライン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();
}

その後、デバッガを介して実行します。実行が指定されたブレークポイントに達すると、プログラムが停止し、制御がデバッガーに戻されます。その後、アセンブリを1ステップ実行して、問題の場所を確認できます。

例外処理スタックアンワインドコードのバグのように見えます。 Dのコンストラクターでインスタンスを使用して単純なクラスEを作成し、呼び出されるかどうかを確認してください。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top