5

これは、C++ でのオブジェクトの初期化に関する質問です。

共通の基本クラスから継承するクラス (インスタンスではない) のグループがあり、プログラムの開始時にコンテナー (具体的にはマップ) に自分自身に関する情報を登録する必要があります。

問題は、動的にする必要があることです。コンテナーは、クラスとは異なる独立したプロジェクトで定義されます。ライブラリを使用する各プログラムのクラスのセットごとに、ライブラリの複数のハードコーディングされたバージョンを作成することは避けたいと思います。

これらのサブクラスのそれぞれに特別なクラスの静的インスタンスを用意して、そのコンストラクターに登録することを考えました。ただし、これらのオブジェクトの構築前にコンテナが構築されることを保証する方法は見つかりませんでした。

サブクラスに関するコンテナ内の情報は、これらのサブクラスのインスタンスが作成される前に利用できる必要があることにも注意してください。

これを行う方法、または一般的に C++ で静的コンストラクターを模倣する方法はありますか?

4

6 に答える 6

2

さまざまな問題を一度に説明しています。ある種の静的初期化を行うという特定の問題については、登録を実行する偽のクラスを作成する簡単な方法があります。次に、さまざまなクラスのそれぞれがstatic const Xメンバーを持つことができます。メンバーは翻訳単位で定義する必要があり、定義はインスタンスのインスタンス化とクラスの登録をトリガーします。

これは、初期化順序の大失敗という難しい問題に取り組んでいません。言語は、異なる翻訳単位でのオブジェクトの初期化の順序について保証しません。つまり、このようなクラスを使用して 3 つの翻訳単位をコンパイルした場合、偽のメンバーの相対的な実行順序は保証されません。これはライブラリにも適用されます。クラスを登録するコンテナーがグローバル/静的メンバー属性である場合、そのコンテナーが初期化されているという保証はありません。

コードにアクセスできる場合は、コンテナー コードを変更して を使用できstatic local variablesます。これは、初期化の順序を確実にするための一歩です。可能な解決策のスケッチとして:

// registry lib
class registry { // basically a singleton
public:
   static registry& instance() { // ensures initialization in the first call
      static registry inst;
       return inst;
   }
// rest of the code
private:
   registry(); // disable other code from constructing elements of this type
};
// register.h
struct register {
   template <typename T>
   register( std::string name ) {
       registry::instance().register( name, T (*factory)() ); // or whatever you need to register
   }
};
// a.h
class a {
public:
   static a* factory();
private:
   static const register r;
};
// a.cpp
const register a::r( "class a", a::factory );
// b.h/b.cpp similar to a.h/a.cpp

この場合、aおよびbクラスの登録に明確な順序はありませんが、問題にはならない可能性があります。一方、関数でローカル静的変数を使用することによりregistry::instanceシングルトンの初期化は、メソッドへの呼び出しの前にregistry::register(メソッドへの最初の呼び出しの一部としてinstance) 実行されることが保証されます。

その変更を行うことができない場合は、基本的に不運でありregistry、他の翻訳単位の他の静的メンバー属性 (またはグローバル) の前にインスタンス化されることを保証できません。その場合、クラスの登録を最初のインスタンス化まで延期し、登録する各クラスのコンストラクターにコードを追加して、オブジェクトの実際の構築前にクラスが登録されるようにする必要があります。

他のコードがその型のオブジェクトを作成するかどうかによって、これが解決策になるかどうかが決まります。ファクトリ関数の特定のケース (最初に頭に浮かんだもの) では、型のオブジェクトを作成することが他に何も許可されていない場合、aまたはb... コンストラクター呼び出しでピギー バッキング登録を行うことも解決策にはなりません。

于 2011-03-15T18:55:52.387 に答える
1

これは OOP パラダイムに反しますが、静的メンバーが 2 つのグローバル変数によって導かれるリンクされたリストを形成するのはどうですか? あなたはそのようなことをすることができます:

ClassRegistrator *head=NULL;
ClassRegistrator *tail=NULL;

struct ClassRegistrator {
    ... //data that you need
    ClassRegistrator *next;
    ClassRegistrator(classData ...) {
      if (head==NULL) head=tail=this;
      else {
        tail->next=this;
        tail=this;
      }
      ... //do other stuff that you need for registration
    }
};


class MyClass { //the class you want to register
    static ClassRegistrator registrator;
}

ClassRegistrator MyClass::registrator(...); //call the constructor

グローバル変数はコンストラクタを必要とせず、純粋なデータであるため、コードの実行を開始するときにすでに初期化されていることが保証されていると思います。

明らかに、これはスレッドセーフなどではありませんが、仕事は完了するはずです。

于 2011-03-15T19:00:53.013 に答える
0

これはSingleton パターンの候補です。基本的に、サブクラスの最初のインスタンスがインスタンス化されたときにコンテナーをインスタンス化する必要があります。これは、基本クラスのコンストラクターでシングルトン ポインターが NULL かどうかを確認することで容易になり、そうであれば、コンテナーをインスタンス化します。

于 2011-03-15T18:47:22.333 に答える
0

1 つのアイデアは、登録ファンクターをクラスに渡すことです。各子孫は登録する関数を実行します。このファンクターは、コンストラクターで渡すことができます。

例:

struct Registration_Interface
{
  virtual void operator() (const std::string& component_name) = 0;
};

struct Base
{
};

struct Child1
  : public Base
{
  Child(Registration_Interface& registration_ftor)
  {
     //...
     registration_ftor("Child1");
  }
};
于 2011-03-15T18:50:12.560 に答える
0

参照: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.14

1 つのオプションは、最初のものが追加されたときに、コンテナーを遅延して構築することです。

  void AddToContainer(...) {
    // Will be initialized the first time this function is called.
    static Container* c = new Container();
    c->Add(...);
  }

静的コンストラクターを「模倣」する唯一の方法は、関数を明示的に呼び出して静的初期化を実行することです。モジュールにリンクするだけでコードをプリメインで実行する方法は他にありません。

于 2011-03-15T18:55:26.693 に答える
0

「初回使用時に初期化」パターンを使用してから、ダミーの静的インスタンスをインスタンス化して、できるだけ早く初期化するようにすることができます。

class cExample
{
    public :
       cExample() ;

       // Static functions here

    private :
        static bool static_init ;

        // other static members here
}

cExample::static init = false ;

cExample::cExample()
{
    // Static initialisation on first use
    if( !static_init )
    {
        // initialise static members
    }

    // Instance initialisation here (if needed)
}

// Dummy instance to force initialisation before main() (if necessary)
static cExample force_init ;
于 2011-03-15T19:28:07.260 に答える