7

このコードから得られる結果に混乱しています。1つのdllでは、静的変数が初期化されるときにカウンターがインクリメントされます。次に、mainが実行されると、このカウンターを読み取りますが、1ではなく0を取得します。誰かがこれを説明できますか?

私のダイナミックライブラリプロジェクトでは:

// Header file
class Foo {
   int i_ = 0;

   Foo(const Foo&) = delete;
   Foo& operator= (Foo) = delete;

   Foo()
   {
   }

public:
   void inc()
   {
      ++i_;
   }

   int geti()
   {
      return i_;
   }

   static Foo& get()
   {
      static Foo instance_;
      return instance_;
   }

   Foo( Foo&&) = default;
   Foo& operator= (Foo&&) = default;
};

int initialize()
{
   Foo::get().inc();
   return 10;
}

class Bar
{
   static int b_;

};

// cpp file
#include "ClassLocalStatic.h"


int Bar::b_ = initialize();

私のアプリケーションプロジェクトでは

// main.cpp
#include <iostream>

#include "ClassLocalstatic.h"

int main(int argc, const char * argv[])
{
   std::cout << Foo::get().geti();
   return 0;
}
4

2 に答える 2

10

C ++の規則では、インライン関数定義は静的ローカル変数で正しく機能すると規定されています。つまり、関数定義をインライン化すると、ローカル静的変数はすべて同じ変数を参照します。

ただし、C++で定義されていないことが1つあります。それはDLLです。

C++仕様はDLLを完全に認識していません。それらをどのように処理するかはわかりません。C ++は、動的ではなく静的リンケージの観点から定義されています。

そのため、これは、DLL境界を処理するときに仕様が適用されなくなったことを意味します。そして、それがあなたの問題の原因です。

C ++では、ローカル静的変数を使用したインライン関数が引き続き機能する必要がありますが、DLLの知識がないC ++は、すべてがコンパイラーの決定に依存していることを意味します。

ローカル静的変数が期待どおりに機能しないようにすることは、DLLの境界を越えて分割されたインライン関数の完全に正当なコンパイラの動作です。これは非常に異常なケースであるため、コンパイラ開発者がそのような事態に備えてコーディングに時間を費やしたことを真剣に疑っています。

コンパイラが実行する最も合理的なことは、DLLヘッダーでexternグローバル変数を宣言した場合に実行することです。各DLLと実行可能ファイルは別々のものを取得します。__declspec(dllexport)そのため、この実行可能ファイル/ DLL( )と他の実行可能ファイル/ DLL()から定義を定義する必要があるという特別な構文が必要です__declspec(dllimport)

DLLの境界を越えて何を置くかについて常に注意する必要があります。一般に、このようにDLLの境界を越えてインライン化しないでください。

于 2012-08-15T10:30:58.260 に答える
10

実行可能ファイルとDLLはどちらもFoo::get()、それぞれ独自の静的変数のコピーを持つ、独自のコピーを取得します。それらは別々のリンカ出力にあるため、リンカは通常のようにそれらを統合できません。

これをさらに拡張するには:

C ++仕様では、すべてが同じ本体を持っている限り、インライン関数を複数の変換単位で定義できます。関数をヘッダーファイルに入れると、各コピーが同一になることが保証されるため、まったく問題ありません。https://stackoverflow.com/a/4193698/5987を参照してください。インライン関数内に静的変数がある場合は、コンパイラとリンカが連携して、それらすべての間で1つのコピーのみが使用されるようにする必要があります。正確なメカニズムはわかりませんが、それは問題ではありません。標準ではそれが要求されています。残念ながら、出力実行可能ファイルまたはDLLが生成された後、リンカの到達範囲が停止し、関数が両方の場所に存在することを認識できません。

修正はFoo::get()、ヘッダーの本文をヘッダーから移動し、DLLのみにあるソースファイルに配置することです。

于 2012-08-15T01:39:49.077 に答える