6

ロード時に多数のクラスをファクトリに登録しようとしています。私の戦略は、静的初期化を利用して、main()が開始する前に、ファクトリが準備ができていることを確認することです。この戦略は、ライブラリを動的にリンクする場合は機能するようですが、静的にリンクする場合は機能しません。静的にリンクすると、静的データメンバーの一部のみが初期化されます。

私の工場が自動車を製造しているとしましょう。少数の車をインスタンス化できるCarCreatorクラスがありますが、すべてではありません。工場でこれらのCarCreatorクラスをすべて収集して、新しい車を探すコードが、実際の建設を誰が行うかを知らなくても工場に行くことができるようにしたいと思います。

だから私は持っています

CarTypes.hpp

enum CarTypes
{
   prius = 0,
   miata,
   hooptie,
   n_car_types
};

MyFactory.hpp

class CarCreator
{
public:
   virtual Car * create_a_car( CarType ) = 0;
   virtual std::list< CarTypes > list_cars_I_create() = 0;
};

class MyFactory // makes cars
{
public:
   Car * create_car( CarType type );
   void factory_register( CarCreator * )

   static MyFactory * get_instance(); // singleton
private:
   MyFactory();

   std::vector< CarCreator * > car_creator_map;
};

MyFactory.cpp

MyFactory:: MyFactory() : car_creator_map( n_car_types );

MyFactory * MyFactory::get_instance() {
   static MyFactory * instance( 0 ); /// Safe singleton
   if ( instance == 0 ) {
      instance = new MyFactory;
   }
   return instance;
}

void MyFactory::factory_register( CarCreator * creator )
{
   std::list< CarTypes > types = creator->list_cars_I_create();
   for ( std::list< CarTypes >::const_iteator iter = types.begin();
         iter != types.end(); ++iter ) {
      car_creator_map[ *iter ] = creator;
   }
}

Car * MyFactory::create_car( CarType type ) 
{
   if ( car_creator_map[ type ] == 0 ) { // SERIOUS ERROR!
      exit();
   }
   return car_creator_map[ type ]->create_a_car( type );
}

..。

次に、特定の車と特定の車の作成者を紹介します。

Miata.cpp

class Miata : public Car {...};

class MiataCreator : public CarCreator {
public:
   virtual Car * create_a_car( CarType );
   virtual std::list< CarTypes > list_cars_I_create();
private:
   static bool register_with_factory();
   static bool registered;
};

bool MiataCreator::register_with_factory()
{
   MyFactory::get_instance()->factory_register( new MiataCreator );
   return true;
}

bool MiataCreator::registered( MiataCreator::register_with_factory() );

..。

繰り返しになりますが、ライブラリを動的にリンクすると、MiataCreator :: registeredが初期化され、ライブラリを静的にリンクしますが、初期化されません。

静的ビルドでは、誰かがMiataを要求するためにファクトリに行くと、willのmiata要素はcar_creator_mapNULLを指し、プログラムは終了します。

プライベート静的積分データメンバーに、初期化が何らかの理由でスキップされるという特別なものはありますか?静的データメンバーは、クラスが使用されている場合にのみ初期化されますか?私のCarCreatorクラスはどのヘッダーファイルでも宣言されていません。それらは完全に.cppファイル内にあります。コンパイラが初期化関数をインライン化し、どういうわけかMyFactory ::の呼び出しを回避している可能性はありますfactory_registerか?

この登録の問題に対するより良い解決策はありますか?

すべてのCarCreatorを単一の関数にリストし、それぞれをファクトリに明示的に登録してから、関数が呼び出されることを保証するオプションはありません。特に、いくつかのライブラリをリンクし、これらの別々のライブラリでCarCreatorsを定義したいのですが、それでも単一のファクトリを使用してそれらを構築します。

..。

これが私が予想しているが私の問題に対処していないいくつかの応答です:

1)シングルトンファクトリはスレッドセーフではありません。a)関係ありませんが、私は単一のスレッドのみを使用しています。

2)CarCreatorsが初期化されているときにシングルトンファクトリが初期化されていない可能性があります(つまり、静的初期化の大失敗があります)a)シングルトンインスタンスを関数に入れることで、安全なバージョンのシングルトンクラスを使用しています。これが問題である場合、MiataCreator's::register_with_factoryメソッドにprintステートメントを追加すると、出力が表示されるはずです。そうではありません。

4

6 に答える 6

8

静的な初期化順序の大失敗があると思いますが、ファクトリではありません。

登録されたフラグが初期化されていないということではなく、十分に早く初期化されていないだけです。

次の場合を除いて、静的初期化順序に依存することはできません。

  1. 同じ変換単位(.cppファイル)で定義された静的変数は、リストされている順序で初期化されます
  2. 変換ユニットで定義された静的変数は、その変換ユニット内の関数またはメソッドが初めて呼び出される前に初期化されます。

信頼できないのは、他の変換ユニットの関数またはメソッドが初めて呼び出される前に、静的変数が初期化されることです。

特に、MyFactory :: create_car(MyFactory.cppで定義)が初めて呼び出される前に、MiataCreator :: registered(Miata.cppで定義)が初期化されることに依存することはできません。

すべての未定義の動作と同様に、必要なものが得られる場合と得られない場合があります。最も奇妙に見える最も無関係なもの(静的リンクと動的リンクなど)は、希望どおりに機能するかどうかが変わる可能性があります。

実行する必要があるのは、Miata.cppで定義されている登録済みフラグの静的アクセサーメソッドを作成し、MyFactoryファクトリにこのアクセサーを介して値を取得させることです。アクセサーは変数定義と同じ変換単位にあるため、変数はアクセサーが実行されるまでに初期化されます。次に、どこかからこのアクセサーを呼び出す必要があります。

于 2009-08-19T16:22:59.927 に答える
3

静的リンクを使用する場合、すべてのオブジェクトファイル(.o)をバイナリに追加することを意味します。これは、動的なものと同じように機能するはずです。(。a)静的ライブラリを作成した場合、リンカーは、内部で使用されているオブジェクトのみとして、それらを内部にリンクしません。静的ライブラリはリンクされており、この場合、明示的に使用されるものはありません。

すべての自動登録手法は、オブジェクトを作成してオンデマンドで返す関数のように、ロード時のコードと静的な大失敗を回避する方法に依存しています。

ただし、それをロードできなければ、オブジェクトファイルのリンクは機能し、ダイナミックライブラリのロードも機能しますが、静的ライブラリは明示的な依存関係がないとリンクされません。

于 2009-08-19T15:53:21.423 に答える
1

通常、静的ライブラリでは、リンカはメインプログラムが参照するライブラリから.oファイルのみを引き出します。MiataCreator :: registeredまたはMiata.cppで実際には何も参照していないが、静的な初期化に依存しているため、静的ライブラリからリンクされている場合、リンカーはそのコードをexeに含めません-

静的にリンクするときに、MiataCreator :: registeredのコードが実際にexeファイルに含まれているかどうかを、nmまたはobjdump(またはWindowsの場合はdumpbin)で結果の実行可能ファイルを確認します。

リンカに静的ライブラリのすべてのビットとピースを含めるように強制する方法はわかりませんが。

于 2009-08-19T16:22:56.667 に答える
1

gccを使用すると、を追加でき-Wl,--whole-archive myLib.a --Wl,--no-whole-archiveます。これにより、参照されていない場合でも、リンカーにオブジェクトが含まれるようになります。ただし、これは移植性がありません。

于 2010-01-30T16:46:46.463 に答える
0

miata要素がマップ内にあるかどうかをいつ確認しますか?メインの前ですか、それとも後ですか?
私が考えることができる唯一の理由は、main()の前にマップ要素にアクセスすることです(グローバル初期化など)。これは、MiataCreator :: registeredの作成前に発生する可能性があります(別の翻訳単位にある場合)

于 2009-08-19T16:11:19.000 に答える
0

個人的には、あなたはリンカーのファウルを許していると思います。

ブール変数は使用されていません'boolMiataCreator :: registered' osリンカーはそれらをlibから実行可能ファイルにプルしていません(実行可能ファイルに関数/グローバルへの参照がない場合、リンカーはそれらのオブジェクトをプルしません。 lib [実行可能ファイルで現在定義されていないオブジェクトのみを検索します])

'bool MiataCreator :: register_with_factory()'にいくつかのprintステートメントを追加して、呼び出されているかどうかを確認できます。または、実行可能ファイルのシンボルをチェックして、そこにあることを確認します。

私がするいくつかのこと:

// Return the factory by reference rather than pointer.
// If you return by pointer the user has to assume it could be NULL
// Also the way you were creating the factory the destructor was never
// being called (though not probably a big deal here) so there was no
// cleanup, which may be usefull in the future. And its just neater.
MyFactory& MyFactory::get_instance()
{
    static MyFactory   instance; /// Safe singleton 
    return instance;
}

オブジェクトを2段階で初期化するのではなく。リンカーが原因で失敗していると思います。ファクトリのインスタンスを作成し、コンストラクタに登録してもらいます。

bool MiataCreator::register_with_factory()
{
     MyFactory::get_instance()->factory_register( new MiataCreator );
     return true;
}
//
// I would hope that the linker does not optimize this out (but you should check).
// But the linker only pulls from (or searches in) static libraries 
// for references that are explicitly not defined.
bool MiataCreator::registered( MiataCreator::register_with_factory() );

私はこれを行います:

MiataCreator::MiataCreator()
{
    // I would change factory_register to take a reference.
    // Though I would store it internall as a pointer in a vector.
    MyFactory::getInstance().factory_register(*this);
}

// In Cpp file.
static MiataCreator   factory;

リンカーはC++オブジェクトとコンストラクターについて知っており、コンストラクターが副作用を引き起こす可能性があるため、すべてのグローバル変数をプルする必要があります(boolも同様に知っていますが、それを最適化するリンカーがいくつかあります)。

とにかくそれは私の2cの価値です。

于 2009-08-19T21:48:23.407 に答える