0

次の形式のさまざまなタイプの派生クラスをファイルからロードしています。

  • 4バイトのクラスIDヘッダー
  • nバイトのシリアル化されたデータ

各クラスは同じ基本クラスから継承されていますが、エレガントに作成するのに問題があります。これは私の現在のコードです(ここではFooとBarは同じタイプから継承されています):

// read class ID
uint32_t id = ReadHeader(myFile);

// create correct class
switch (id)
{
    case ID_CLASS_FOO: myClass = new Foo(myFile); break;
    case ID_CLASS_BAR: myClass = new Bar(myFile); break;
    /* ... */
}

しかし、これはかなり醜く、退屈で、エラーが発生しやすいと思います。追加するクラスごとに、1つの追加のdefine / enumメンバーと、スイッチに1つの追加行が必要だからです。

私が探しているのは、コンパイル時の「型配列」を次のように宣言するものです。

ClassTypes = {Foo, Bar, ...};

そして、ファイルを読むときは、次のようにします。

myClass = new ClassTypes[id](myFile);

C ++でこれを行う方法はありますか?

4

3 に答える 3

2

ファクトリクラスを作成できます。

工場定義:

typedef ClassType* (*ClassCreation(void))

class ClassFactory
{
    private:
        map<ClassId, ClassCreation> creators;

    public:
        ClassFactory()
        {
            creators[ID_CLASS_FOO] = &Foo::create;
            creators[ID_CLASS_BAR] = &Bar::create;
        }

        ClassType* getClassType(ClassId id)
        {
            return (creators[id])()
        }
};

class ClassType
{
    //etc
};

class Foo : public ClassType
{
    public:
        static ClassType* create()
        {
            return new Foo;
        }
};

class Bar : public ClassType
{
    public:
        static ClassType* create()
        {
            return new Bar;
        }
};

工場での使用:

ClassFactory factory;

uint32_t id = ReadHeader(myFile);

ClassType* myClass = factory.getClassType(id);
于 2012-10-13T02:28:31.523 に答える
0

次のような基本クラスの静的関数についてはどうでしょうか。

ClassTypes create(FileType myFile)
{
    // read class ID
    uint32_t id = ReadHeader(myFile);

    // create correct class
    switch (id)
    {
        case ID_CLASS_FOO: myClass = new Foo(myFile); break;
        case ID_CLASS_BAR: myClass = new Bar(myFile); break;
        /* ... */
    }

    return myClass;
}

少なくともそのように、代わりに

myClass = new ClassTypes[id](myFile);

あなたができる

myClass = ClassTypes::create(myFile);
于 2012-10-13T02:17:51.073 に答える
0

IDが0、1、2、3、...になると仮定すると、std::map各メッセージIDを、そのIDに適切な種類のオブジェクトを作成する名前付きコンストラクターへの関数ポインターにマップするを作成できます。

class BaseClass {
   private:
   typedef (BaseClass*) (*NamedConstructor) (SomeType &);

   // The map.
   static std::map<int, NamedConstructor> id_to_constructor;

   public:

   // All the possible message types.
   enum MessageType {
      FooMsg = some_value,
      BarMsg = some_other_value,
      ... // potentially a whole lot more
   };

   // Add a named constructor to the map.
   static void add_handler (MessageType id, NamedConstructor cotr) {
      // Error handling such as duplicates left as an exercise to the user.
      id_to_constructor[id] = cotr;
   }

   // Function that applies the map.
   static void handle_message (int id, SomeType & my_file) {
      // Error handling such as a missing entry left as an exercise to the user.
      NamedConstructor cotr = id_to_constructor[id];
      cotr (my_file);
   }
   ...
};


class Foo : public BaseClass {
   public:
   static BaseClass* create_foo (SomeType & my_file) {
      return new Foo (my_file); // Or use a smart pointer.
   }

   // Member data and member functions elided.
   ...
};

class Bar : public BaseClass {
   public:
   static BaseClass* create_bar (SomeType & my_file) {
      return new Bar (my_file); // Or use a smart pointer.
   }

   // Member data and member functions elided.
    ...
};

メソッド を使用して名前付きコンストラクターなどをFoo::create_foo()登録するには、何らかのメカニズムが必要です。500のメッセージタイプがある場合、それは500行のコードですが、直線(ifなし、スイッチなし)のコードになります。Bar::create_bar()BaseClassadd_handler

別の方法は、500ケースのswitchステートメントです。うん。

では、なぜベクトルではなくマップなのか?IDが0、1、2、...になることがわかっている場合は、ベクトルで問題ありません。ギャップがある場合はどうなりますか?設計上、大きなギャップがある場合はどうなりますか?たとえば、メッセージIDは、エラーを減らすためにハミング符号化されている場合があります。

于 2012-10-13T03:13:09.697 に答える