4

C++ でファクトリ クラスを実装する必要があるのですが、それについて考えていたときに、解決できない大きな問題が 1 つ見つかり、周りのファクトリ実装の例はすべて同じように欠陥があることがわかりました。私が悪いのかもしれませんが、理由を教えてください。

したがって、ここに単純な「典型的な」ファクトリ実装があります。これにより、Factory クラスを変更せずに新しいオブジェクトを登録できます。

//fruit.h
class Fruit
{
protected :
  int count;
public :
  Fruit(int count) : count(count) {}
  virtual void show() = 0;
};

// factory.h
/** singleton factory */
class Factory
{
  typedef Fruit* (*FruitCreateFunction)(int);
  static Factory* factory;
  std::map<std::string, FruitCreateFunction> registeredFruits;
public :
  static Factory& instance()
  {
    if (factory == NULL)
      factory = new Factory();
    return *factory;
  }
  bool registerFruit(const std::string& name, Fruit* (createFunction)(int))
  {
    registeredFruits.insert(std::make_pair(name, createFunction));
    return true;
  }
  Fruit* createFruit(const std::string& name, int count)
  {
    return registeredFruits[name](count);
  }
};

//factory.cpp
Factory* Factory::factory = NULL;

//apple.h
class Apple : public Fruit
{
  static Fruit* create(int count) { return new Apple(count); }
  Apple(int count) : Fruit(count) {}
  virtual void show() { printf("%d nice apples\n", count); };  
  static bool registered;
};

// apple.cpp
bool Apple::registered = Factory::instance().registerFruit("apple", Apple::create);

//banana.h
class Banana : public Fruit
{
  static Fruit* create(int count) { return new Banana(count); }
  Banana(int count) : Fruit(count) {}
  virtual void show() { printf("%d nice bananas\n", count); };  
  static bool registered;
};

// banana.cpp
bool Banana::registered = Factory::instance().registerFruit("banana", Banana::create);

// main.cpp
int main(void)
{
  std::vector<Fruit*> fruits;
  fruits.push_back(Factory::instance().createFruit("apple", 10));
  fruits.push_back(Factory::instance().createFruit("banana", 7));
  fruits.push_back(Factory::instance().createFruit("apple", 6));
  for (size_t i = 0; i < fruits.size(); i++)
    {
      fruits[i]->show();
      delete fruits[i];
    }
  return 0;
}

わかりました、このコードは派手に見えて機能しますが、ここに来るのは次のとおりです。

C++ 標準では、グローバル (静的) 変数を定義する順序を定義できません。

ここに3つの静的変数があります

Apple::registered;
Banana::registered;
Factory::factory;

ポインターは、Apple(または Banana)::registered 変数のFactory::factoryに NULL に定義する必要があります。そうしないと、メソッドが初期化されていない値で動作し、予期しない動作をします。Factory::instance

それで、私はここに来ていないのですか?コードは偶然だけで本当に動いているのでしょうか? もしそうなら、どうすれば問題を解決できますか?

4

3 に答える 3

11

すべてのグローバル POD データは、初期化子が実行される前に定数値に初期化されることが保証されています。

したがって、プログラムの開始時、レジスタ呼び出しが行われる前、および main が実行される前に、ポインターは NULL になり、ブール値はすべて自動的に false になります。次に、登録呼び出しを含む初期化子が実行されます。

編集:具体的には、標準から(3.6.2.2:非ローカルオブジェクトの初期化):

ゼロ初期化と定数初期化をまとめて静的初期化と呼びます。他のすべての初期化は動的初期化です。静的初期化は、動的初期化が行われる前に実行されます。

于 2010-10-17T02:24:53.910 に答える
4

すべての静的変数は、プログラムの実行が開始される前に初期化されます。それらはコンパイル時に設定され、実行可能ファイルに直接焼き付けられます。

1 つの静的変数が別の静的変数に依存する場合にのみ問題が発生します。

a.hpp で:

static int a = 1;

b.hpp:

extern int a;
static int b = a;

静的変数が初期化される順序は明確に定義されていないため、この例では b が 1 である場合とそうでない場合があります。変数が相互に依存しない限り、問題ありません。さらに、初期値を指定しない場合、静的メンバーはデフォルトでゼロに設定されます。

于 2010-10-17T02:27:12.223 に答える
2

Factory の「インスタンス」メソッドが次のように実装されているのを見る傾向がありました。

static Factory& instance()
{
    static Factory *factory = new Factory();
    return *factory;
}

ただし、ポイントは、インスタンスへのすべてのアクセスが静的インスタンス メソッドを介して実行されることです。たとえば、2 つのフルーツ クラスを登録する呼び出しでは、Factory::instance() を使用して、Factory::factory の初期化子が実行されたことを保証するシングルトンを取得します。私の投稿された代替実装では、静的初期化はメソッドが最初に呼び出されたときにのみ発生します。

Apple::registered と Banana::registered で起こりうる問題は、それらがどこから使用されるかによって異なります。投稿されたコードでは、それらはまったく使用されていません。それぞれapple.cppとbanana.cpp内でのみ使用される場合、初期化の順序に問題はありません。

于 2010-10-17T02:47:20.703 に答える