7

したがって、MSVCRT の静的にリンクされたコピーを含む C++ ライブラリがあります。誰でも私のライブラリをどのバージョンの MSVC ランタイムでも使用できるようにしたいと考えています。この目標を達成するための最良の方法は何ですか?

私はすでに物事がどのように行われるかについてかなり注意を払っています。

  1. メモリが DLL バリアを通過して解放されることはありません
  2. ランタイム C++ オブジェクトは、バリア (つまり、ベクター、マップなど) を越えて渡されません。バリアのその側で作成された場合を除きます)。
  3. バリア間でファイル ハンドルまたはリソース ハンドルが渡されない

それでも、ヒープの破損を引き起こす単純なコードがまだいくつかあります。

ライブラリに次のようなオブジェクトがあります。

class Foos
{
public: //There is an Add method, but it's not used, so not relevant here
    DLL_API Foos();
    DLL_API ~Foos();

private:
    std::map<std::wstring, Foo*> map;
};

Foos::~Foos()
{
    // start at the begining and go to the end deleting the data object
    for(std::map<std::wstring, Foo*>::iterator it = map.begin(); it != map.end(); it++)
    {
        delete it->second;
    }
    map.clear();
}

そして、次のようにアプリケーションから使用します。

void bar() {
    Foos list;
}

どこからでもこの関数を呼び出すと、スタックの破損に関するデバッグ警告が表示されます。そして、実際に使い果たした場合、実際にはスタックとセグメンテーション違反が破損します。

私の呼び出し元アプリケーションは、Visual Studio 2012 プラットフォーム ツールでコンパイルされています。ライブラリは、Visual Studio 2010 プラットフォーム ツールを使用してコンパイルされます。

これは絶対にやってはいけないことですか、それとも実際に複数のランタイムを使用するための規則に違反していますか?

4

4 に答える 4

9

メモリが DLL バリアを通過しない

しかし、そうです。実際何度も。アプリケーションは、クラス オブジェクトのストレージを (この場合はスタック上に) 作成しました。次に、ライブラリ内のメソッドへのポインターを渡します。コンストラクター呼び出しから始めます。そのポインターは、ライブラリ コード内のthisです。

このようなシナリオでうまくいかないのは、正しい量のストレージが作成されなかったことです。VS2012 コンパイラにクラス宣言を確認してもらいました。std::map の VS2012 実装を使用します。ただし、ライブラリはVS2010でコンパイルされており、std::mapの完全に異なる実装を使用しています。全然違うサイズで。C++11 による大幅な変更。

これは作業中の完全なメモリ破損です。スタック変数を書き込むアプリケーション内のコードは、std::map を破損します。そしてその逆です。

モジュールの境界を越えて C++ クラスを公開することは、そのようなトラップでいっぱいです。すべてがまったく同じコンパイラ バージョンとまったく同じ設定でコンパイルされることを保証できる場合にのみ、これを検討してください。ショートカットはありません。デバッグとリリースのビルド コードを混在させることもできません。実装の詳細が公開されないようにライブラリを作成することは確かに可能ですが、次の規則に従う必要があります。

  • 仮想メソッドを持つ純粋なインターフェイスのみを公開します。引数の型は、単純な型またはインターフェイス ポインターである必要があります。
  • クラス ファクトリを使用してインターフェイス インスタンスを作成する
  • メモリ管理に参照カウントを使用して、常にライブラリがリリースされるようにします。
  • パッキングや呼び出し規約などのコア詳細を厳格なルールで明確にします。
  • 例外がモジュールの境界を越えることは決して許可せず、エラー コードのみを使用してください。

それまでには、COM コードや、DirectX などで使用されているスタイルを書くことができるようになっているでしょう。

于 2013-11-14T16:44:54.350 に答える
3

ランタイム C++ オブジェクトは、バリア (つまり、ベクター、マップなど) を越えて渡されません。ただし、それらがバリアの側で作成された場合を除きます)。

あなたはまさにそれをやっています。Foos オブジェクトはメイン プログラムによってスタック上に作成され、ライブラリで使用されています。オブジェクトにはマップが含まれています...

メイン プログラムをコンパイルすると、ヘッダー ファイルなどを調べて、Foos オブジェクトに割り当てるスタック スペースの量を決定します。そして、ライブラリで定義されているコンストラクターを呼び出します...オブジェクトのまったく異なるレイアウト/サイズを期待していた可能性があります

于 2013-11-14T16:13:43.970 に答える
3

mapメンバー変数は、DLL ではなくアプリケーションによって割り当てられた一部の内部データを使用してアプリケーションによって作成されます (また、異なる実装を使用する場合がありますmap)。経験則として、DLL のスタック オブジェクトを使用せず、DLL に次のようなものを追加Foos * CreateFoos()します。

于 2013-11-14T15:59:01.350 に答える
0

あなたのニーズに合わないかもしれませんが、ヘッダーファイルにすべてを実装すると問題が単純化されることを忘れないでください:-)

于 2013-11-14T19:23:03.487 に答える