1

今まで見たことのない非常に奇妙な行動を見つけました。私は複雑な VS2005 C++ プロジェクトに取り組んでいます。

class Tester
{
public:
    Tester()
    {
        TRACE("Construct Tester");
    }
    ~Tester()
    {
        TRACE("~Destruct Tester");
    }
};

void Thrower()
{
    Tester X;
    throw std::exception("Booom");
}

Thrower()が呼び出されたときに、Trace 出力に何が表示されることを期待していますか? そのテスターは構築され、スタックが巻き戻されたときに破棄されますか?

少なくとも私はそれを期待していますが、Tester のデストラクタは決して呼び出されません!

不可能 !?!?!?!

これは Visual Studio のバグですか?

私はたくさん検索しましたが、Stackoverflow でも答えが見つかりませんでした。

4

1 に答える 1

1

何が悪いのかを見つけるのに丸一日かかりました。

ここで、私が何をしているのかをもう少し詳しく説明する必要があります。LIB ファイルにコンパイルする C++ コードがあります。上記のコード (Tester と Thrower) は、このプレーンな C++ LIB ファイルにあります。

そして、この LIB ファイルにリンクするマネージド C++ DLL にコンパイルされる別の C++ コードがあります。したがって、最後に両方のコードが同じ DLL 内にあります。次のように、LIB ファイル内のコードを呼び出すラッパー関数を管理しました。

ManagedWrapper()
{
    try
    {
        Thrower();
    }
    catch (std::exception& e)
    {
        throw new System::Exception(e.what());
    }
}

これは機能しません。このコードでは、メモリ リークとネットワーク ソケットが閉じられていません。Thrower のスタックは巻き戻されません。

これは、catch に到達する前にスタックの巻き戻しが行われないためです。ただし、catch が throw 以外の別のライブラリにある場合、スタックは巻き戻されません。DLL は LIB ファイルのスタックをアンワインドする方法を知りません (両方とも最終的に同じ DLL にコンパイルされますが!!)

しかし、私は非常に簡単な解決策を見つけました。

LIB ファイルでは、次のように ManagedWrapper() と Thrower() の間に中間関数を追加する必要がありました。コードはばかげているように見えますが、問題は解決します。

Catcher()
{
    try
    {
         Thrower();
    }
    catch(...) // unwind HERE
    {
        throw;
    }
}

重要なことは、このキャッチャーは、例外がスローされる LIB ファイルに存在する必要があるということです。例外がキャッチされると、スタックがアンワインドされ、マネージド ラッパーに例外が再スローされます。

ばかげているように見えるコードが非常に賢い場合もあります。

概要: 例外は常に、例外がスローされたのと同じライブラリでキャッチする必要があることを忘れないでください。それらがライブラリの境界を越えてキャッチされると、重大な問題が発生します。

于 2014-01-25T03:56:27.083 に答える