2012-10-13 10 views
0
I의 형식은 파일로부터 파생 된 클래스의 종류, 로딩하고

:C++ 클래스 타입 배열

  • 4 바이트 클래스 ID 헤더
  • N 바이트 직렬화 된 데이터

각 클래스는 동일한 기본 클래스에서 상속 받지만 우아하게 만드는 데 어려움이 있습니다. 이것은 (여기 푸과 바는 동일한 유형에서 상속) 내 현재 코드 :

// 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; 
    /* ... */ 
} 

하지만 추가 할 때마다 별도의 클래스, 내가 하나 더 필요하기 때문에 나는이 오히려 못생긴 지루하고 발생하기 쉬운 오류를 찾을 define/enum 구성원 및 스위치에 하나의 추가 행이 있습니다.

은 내가 무엇을 찾고 있어요 것은 내가 컴파일시 "유형의 배열"등처럼 선언 할 것입니다 무언가이다 : 파일을 읽을 때

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

그 다음을, 그냥 가서 :

myClass = new ClassTypes[id](myFile); 

C++에서이를 수행 할 수있는 방법이 있습니까?

답변

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); 
0

무엇과 같이 보이는 당신의 기본 클래스의 정적 기능에 대한 : 당신이 할 수있는 대신

myClass = new ClassTypes[id](myFile); 

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 = ClassTypes::create(myFile); 
+0

그럼 여전히 다른 문제가 있습니다. 그냥 다른 장소로 이동했습니다 ... – Thomas

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. 
    ... 
}; 

당신은 BaseClass 방법 add_handler를 사용하는 등, 명명 된 생성자 Foo::create_foo(), Bar::create_bar()을 등록 할 몇 가지 메커니즘이 필요합니다. 500 개의 메시지 유형이있는 경우 500 줄의 코드이지만 직선 (전환 할 수없는 경우) 코드가됩니다.

대신 500 건의 switch 문이 있습니다. Yech.

왜 벡터가 아닌 맵입니까? ID가 0, 1, 2, ...가 될 것이라는 것을 알고 있으면 벡터가 문제가되지 않습니다. 만약 당신이 틈이 있다면? 설계 상 큰 격차가 있다면 어떨까요? 예를 들어, 메시지 ID는 오류를 줄이기 위해 해밍 - 인코딩 될 수 있습니다.

+0

좋아요.내가 게시 한 예와 너무 많이 다르게 나타나지는 않습니다. 새로운 하위 유형을 동적으로 추가하는 것과 같은 몇 가지 추가 기능이 있습니다. –