2012-06-05 6 views
0

일부 값 (A, B)을 등록하는 두 클래스 (예 : 클래스 ARegister, 클래스 BRegister)를 초기화하려고합니다. 슈퍼 클래스 (?)에서 두 클래스를 초기화하고 싶습니다.다른 클래스의 초기화 클래스 - C++

예 : RegisterALL 클래스는 ARegister 및 BRegister를 초기화합니다. 따라서 모듈을 사용하는 모든 사용자는 ARegister 및 BRegister 객체를 개별적으로 만들 필요가 없으며 RegisterALL 객체를 만들 수 있습니다.

나는 이러한 모든 등록을 생성자에서 수행하고자합니다. 따라서 수행해야 할 모든 작업은 RegisterALL 클래스의 객체를 만드는 것이며 등록은 자동으로 수행됩니다.

우리는 프로젝트에서 예외를 사용하지 않습니다. 따라서 등록에 오류가 있으면 알 수 없습니다.

등록을 수행하거나 생성자가 등록하도록 별도의 멤버 함수를 사용해야합니까?

나는 OO 설계에 초보자입니다. 디자인에 뭔가 잘못되었다고 생각합니다. 올바른 접근 방식을 지적 할 수 있다면 도움이 될 것입니다.

+0

돌아가서 대답을 수락 해주십시오. 이것은 사람들이 당신을 도우라고 권합니다. – Hassan

+0

"프로젝트에서 예외를 사용하지 않습니다." 이것에 대한 좋은 이유가 있습니까? 모든 예외를 비활성화 하시겠습니까? (예 :'new'가 실패하면 throw합니까?)'NULL'을 반환합니까?'new '를 사용하지 않습니까?) –

+0

예, 우리는 "new"를 사용합니다. "new"로 객체를 초기화 한 후 NULL을 확인합니다. – Saaras

답변

3

가장 좋은 방법은 CRTP (기이하게 반복되는 템플릿 패턴)를 사용하는 것이고, 파생 클래스 인 ARegister와 BRegister는 자신을 템플릿 인수로 RegisterALL 기본 클래스에 전달합니다. 은 다음과 같을 것이다 :

class RegisterAll 
{ 
public: 
    template<class DerivedType> 
    DerivedType *createType() 
    { 
     RegisterAll *r = (*(m_creators[DerivedType::id]))(); 
     return dynamic_cast<DerivedType *>(r); //static_cast will work too if you didn't make a mistake 
    } 
protected: 
    static std::map<int,RegisterAll *(*)()> m_creators; 
}; 

std::map<int,RegisterAll *(*)()> RegisterAll::m_creators = std::map<int,RegisterAll *(*)()>(); 

template<class Derived> 
class CRTPRegisterAll : public RegisterAll 
{ 
public: 
    static bool register() 
    { 
     RegisterAll::m_creators.push_back(std::make_pair(Derived::id,Derived::create); 
     return true; 
    } 

private: 
    static bool m_temp; 
}; 

template<class Derived> 
bool CRTPRegisterAll<Derived>::m_temp = CRTPRegisterAll<Derived>::register(); 

class RegisterA : public CRTPRegisterAll<RegisterA> 
{ 
private: 
    static RegisterA *create() 
    { 
     //do all initialization stuff here 
     return new RegisterA; 
    } 

public: 
    static const int id = 0; 
}; 

지금 CRTPRegisterAll있는 정적 변수 m_temp의 초기화 RegisterAll의리스트 상에 각각 유도 유형 제작자 함수 푸시. RegisterAll에 모든 파생 클래스를 알리는 것은 일반적으로 그리 좋은 디자인이 아닙니다 (확장 성이 매우 뛰어남). 이렇게 생성 된 각 클래스에서 실제 생성 메소드를 구현할 수 있으며 작성자 함수는 RegisterAll에 자동으로 등록됩니다. CRTP의 가장 큰 장점 중 하나는 파생 클래스가 기본 클래스 목록에 자신을 등록하는 방법을 알 필요가 없다는 것입니다. 오류 처리에 관해서도 각 파생 클래스의 생성자 함수에서 구현할 수 있습니다. id 문제를 처리하는 더 좋은 방법이 있지만 여기에 들어가는 것은 싫어합니다. 간단한 방법을 원한다면 공장 디자인 패턴을 읽어보십시오.

+0

+1 초보자에게는 너무 복잡하지만 그럼에도 불구하고 배우는 것은 좋은 속임수입니다. – Jake

+0

초보자는 복잡한 트릭을 배워서 옛날이되어 간다. :) –

+0

고마워, 이거 멋지다. – Saaras

3

이미 개체간에 어떤 종류의 관계가 있다고 판단한 것 같습니다. 하지만, 당신은 막연하게 관계를 묘사합니다.

RegisterALL이 단순 봉쇄를 사용하는 경우 매우 간단한 관계가됩니다. 이 관계는 다음과 같이 표현 될 수 있습니다 (ASCII 그래픽을 사용하십시오) :

 +-------------+ 
     | RegisterALL |    --> := has 
     +-------------+ 
      |  | 
      v  v 
+-----------+ +-----------+ 
| ARegister | | BRegister | 
+-----------+ +-----------+ 

이점은 두 부양 가족에 대한 그림이 매우 간단하다는 것입니다. 그러나 많은 개체를 등록하는 경우 그림은 XRegister 개체의 무리로 폭발하는 RegisterALL처럼 보이기 시작합니다.

RegisterALL 경우는 ARegisterBRegister, 당신은 RegisterALL 컨테이너를 유지 할 수 있도록 ARegisterBRegister위한 공통 기본 클래스를 생성 할 수 있습니다 포함하기위한 것입니다.

 +-------------+  +------------------+   <>--> := aggregates 
     | RegisterALL |<>--->| AbstractRegister |    
     +-------------+  +------------------+    | 
            |      _/_\_ := inherits 
           /\ 
           ___/___\___ 
           |   | 
        +-----------+  +-----------+ 
        | ARegister |  | BRegister | 
        +-----------+  +-----------+ 

우리는 많은 새로운 항목이 등록되는 방법을 상관없이, RegisterALLAbstractRegister 사이의 관계는 동일하게 유지 것을 알 수있다. 한발 더 나아가 템플릿에서 ARegisterBRegister을 유도 할 수 있습니다.

 +-------------+  +------------------+ 
     | RegisterALL |<>--->| AbstractRegister | 
     +-------------+  +------------------+ 
            | 
           /\ 
            /___\ 
            | 
            |  +--------------+ 
          +--------------| RegisterType | 
          |    +--------------+ 
          | RegisterTemplate | 
          +------------------+ 

좋아, 너무 많은 OO 디자인 수업. 이것은 코드로 변환됩니다. 쉬운 일부터 시작합시다. RegisterType은 등록 할 여러 항목을 열거합니다. RegisterTypeName과 오버로드 된 연산자를 사용하면 RegisterType을 인쇄 할 때 코드가 숫자 대신 문자열을 인쇄 할 수 있습니다.

enum RegisterType { A, B, MAX_RegisterType }; 

static inline std::string 
RegisterTypeName (RegisterType t) 
{ 
    static const char * names[] = { "A", "B" }; 
    return names[t]; 
} 

static inline std::ostream & 
operator << (std::ostream &output, RegisterType t) 
{ 
    return output << RegisterTypeName(t); 
} 

AbstractRegister은이 유형을 쿼리하는 인터페이스를 제공합니다. 또한 poke 인터페이스에는 기본 구현이 제공됩니다. C++에서 추상 형식은 가상 소멸자를 제공해야합니다.

class AbstractRegister { 
public: 
    virtual ~AbstractRegister() {} 
    virtual RegisterType type() const = 0; 
    virtual void poke() { std::cout << "Poked " << type(); } 
}; 

typedef std::unique_ptr<AbstractRegister> AbstractRegisterPtr; 
static const AbstractRegisterPtr AbstractRegisterNullPtr; 

RegisterALL 클래스 형 AbstractRegister의 물건을 보유하는 용기가있다. 지도를 사용하여 AbstractRegister 인스턴스에 RegisterType 인스턴스를 연결합니다.이 인스턴스는 등록하려고합니다. RegisterALL은 하나의 인스턴스 만 허용한다는 의미에서 싱글 톤으로 구현됩니다. add 메서드는 등록을 수행하고 find 메서드를 사용하면 등록 된 인스턴스를 찾을 수 있습니다. 의 정의가 끝날 때까지 RegisterALL 생성자의 구현이 연기됩니다.

class RegisterALL { 
    template <RegisterType> friend class RegisterTemplate; 
    typedef std::unique_ptr<RegisterALL> SelfPtr; 
    typedef std::map<RegisterType, AbstractRegisterPtr> RegisterMap; 
    void add (AbstractRegister *r) { all[r->type()] = AbstractRegisterPtr(r); } 
    RegisterALL(); 
public: 
    static const SelfPtr & instance() { 
     if (!one) new RegisterALL; 
     return one; 
    } 
    const AbstractRegisterPtr & find (RegisterType t) const { 
     RegisterMap::const_iterator i = all.find(t); 
     return (i != all.end()) ? i->second : AbstractRegisterNullPtr; 
    } 
private: 
    static SelfPtr one; 
    RegisterMap all; 
}; 

RegisterALL::SelfPtr RegisterALL::one; 

RegisterTemplate 클래스 AbstractRegister에서 유래하고 RegisterType 의해 파라미터 화된다. 템플릿 매개 변수의 값을 반환하여 type 가상 메서드를 구현합니다. 또한 싱글 톤을 사용하지만 인스턴스를 공개하지는 않습니다. 대신 인스턴스는 RegisterALL으로 관리됩니다. 그것은 자신을 RegisterALL으로 등록하는 register_type 메소드를 제공하며이 인스턴스는 RegisterALL에있는 find 메소드를 사용해야 만 찾을 수 있습니다.

template <RegisterType RT> 
class RegisterTemplate : public AbstractRegister { 
    RegisterType type() const { return RT; } 
    void poke() { 
     std::cout << "Poked " << RegisterTypeName(RT) << std::endl; 
    } 
    RegisterTemplate() { 
     std::cout << "Created " << RegisterTypeName(RT) << std::endl; 
    } 
    ~RegisterTemplate() { 
     std::cout << "Destroying " << RegisterTypeName(RT) << std::endl; 
    } 
public: 
    static void register_type() { 
     if (RegisterALL::instance()->find(RT)) { 
      std::cout << "Already registered " << RegisterTypeName(RT) 
         << std::endl; 
      return; 
     } 
     RegisterALL::instance()->add(new RegisterTemplate<RT>); 
    } 
}; 

RegisterALL 생성자 헬퍼 register_all 템플릿을 이용하는 것을 RegisterType ENUM를 반복하고, 이에 모든 RegisterType 년대 RegisterALL가 등록되게 해당 RegisterTemplate 인스턴스화.

그래서
template <unsigned X> 
struct register_all { 
    register_all() { 
     RegisterTemplate<static_cast<RegisterType>(X)>::register_type(); 
     register_all<X+1>(); 
    } 
}; 

template <> struct register_all<MAX_RegisterType> {}; 

inline RegisterALL::RegisterALL() 
{ 
    one = std::move(SelfPtr(this)); 
    register_all<0>(); 
} 

그것을 밖으로 시도, 우리는 다음 코드를 사용합니다

RegisterALL::instance();     // registers all RegisterType's 
RegisterTemplate<B>::register_type();  // try to register B again 
RegisterALL::instance()->find(A)->poke(); // poke at A 

을 그리고 이것은 출력 :

Created A 
Created B 
Already registered B 
Poked A 
Destroying B 
Destroying A 

공지 사항 스마트 포인터가 자동으로 등록 된 항목을 정리하는 방법 우리를 위해.

+0

코드에서 배울 점이 많습니다. 나는 2 개의 대답을받는 방법을 모르겠다. – Saaras

+0

@Saaras : 그것을 땀 나게하지 마십시오. 질문에 대한 최상의 답을 제공한다고 생각하는 것을 골라보십시오. – jxh

+0

@Saaras :이 예제를 완료했습니다. 질문이 있으면 알려주세요. – jxh

관련 문제