2013-09-10 7 views
0

단일 UI 클래스에서 상속 된 일련의 클래스를 관리하는 UIManager가 있습니다.템플릿 및 지연 초기화

class UIManager 
{ 

public: 
    UIManager();   // Constructor 
    virtual ~UIManager(); // Destructor 


    template <typename T> 
    T *getUI() 
    { 
     static T ui();  // Constructs T, stores result in ui when 
          // getUI<T>() is first called 
     return &ui;  
    } 
} 

로 호출 :

getUI<NameEntryUI>()->activate(); 

또는

getUI<MenuUI>()->render(); 

내가 디자인을 고려하고 현재는 개별 사용자 인터페이스가 느리게 초기화하고 정적으로 저장됩니다 이런 식으로 뭔가를 작동 변경하면 하나 이상의 플레이어, 따라서 하나 이상의 게임 창, 따라서 하나 이상의 UIManager를 가질 수 있습니다. UIManager가 삭제 될 때 모든 생성 된 ui 객체를 정리하기를 원합니다 (현재는 ui 객체가 정적이기 때문에 프로그램이 종료 될 때까지 고정되어 있습니다).

UIManager가 종료되었을 때 위의 개체를 제거하려면 어떻게 다시 작성할 수 있습니까? 여기 ======================================

이 해결책 구현했습니다. 초기 결과는 잘 작동한다는 것입니다.

기본적으로 나는 Potatoswatter가 제안한 아이디어로 시작했습니다. Potatoswatter는 내가 좋아하는 방식으로, typeid (T)에 대해 몰랐기 때문에 내가 시작한 방식과 비슷했기 때문에 좋아했습니다. C++ 98 기능 만 사용하도록 코드를 백 포트했습니다. 전체적으로 중요한 것은 typeid (T)입니다.이 인터페이스를 사용하면 인스턴스화 된 인터페이스를 해당 유형에 일관된 방식으로 매핑 할 수 있습니다.

class UIManager 
{ 
    typedef map<const char *, UserInterface *> UiMapType; 
    typedef UiMapType::iterator UiIterator; 

    map<const char *, UserInterface *> mUis; 

public: 
    UIManager();   // Constructor 
    virtual ~UIManager() // Destructor 
    { 
     // Clear out mUis 
     for(UiIterator it = mUis.begin(); it != mUis.end(); it++) 
     delete it->second; 

     mUis.clear(); 
    } 

    template <typename T> 
    T *getUI() 
    { 
     static const char *type = typeid(T).name(); 

     T *ui = static_cast<T *>(mUis[type]); 
     if(!ui) 
     ui = new T(); 

     mUis[type] = ui; 

     return ui; 
    } 
} 

답변

2

현재 각 유형의 하나의 UI 요소에 할당 된 저장 공간 만 있습니다. 이 원칙을 지키기는 근본적으로 불가능하지만 여러 창이 있습니다.

신속하고 지저분한 해결책은 창 번호에 대한 템플릿 인수를 추가하는 것입니다. 게임이고 제한된 수의 플레이어 만있는 경우 미리 결정된 수의 창에 대해 정적 스토리지를 보유 할 수 있습니다.

template <typename T, int N> 
T *getUI() 

타입 시스템 UI ID를 묶는 접근하지만, 근본적으로 결함이고, I는 다형성 및 용기를 사용하여 더 많은 종래의 방법을 권장합니다. , 유형별로 개체를 식별, 아직 동적으로 저장하는


한 가지 방법은이 가상 소멸자를 가지고 UIBase을 필요로

class UIManager { 
    std::map< std::type_index, std::unique_ptr<UIBase> > elements; 

    template< typename T > 
    T & GetUI() { // Return reference because null is not an option. 
     auto & p = elements[ typeid(T) ]; 
     if (! p) p.reset(new T); 
     return dynamic_cast< T & >(* p); 
    } 
} 

주처럼 보일 수도 있고, 객체가 종료되지 않습니다 당신이 그만 둘 때.

+0

'std :: shared_ptr '을 저장하면 유형에 공통 기본 클래스 나 가상 소멸자가 필요하지 않음을 의미합니다. –

+1

@JonathanWakely 아무 것도 공유하지 않기 때문에 약간의 학대가 일어납니다. 진심으로, 다형성이없는 UI는 광란입니다. 무의미한 행동을 유도하고 클릭에 응답하는 가상의 방법이 없다면, 프로그램 조직에 심각한 문제가 있습니다. – Potatoswatter

+0

내 질문에 언급했듯이, 모든 내 UI 개체는 일반적인 부모로부터 상속받습니다 (여기에는 정신 나간 것이 없습니다 :-). 난 그런지도를 만들려고했지만 unique_ptr을 사용하지 않을 생각이었습니다. 유일한 단점은 unique_ptr이 C++ 11 항목이며 아직 완전히 포용하지 못했다는 것입니다. – Watusimoto

1

형식 당 여러 개의 개체가 분명히 필요하므로 개체를 std::map<UIManager const*, T>에 저장하면됩니다. 특정 관리 객체를 가져 오려면 맵에서 해당 유형에 대해 조회합니다. 까다로운 비트 이상 기능 개체의 목록을 이용하여 처리 된 객체를 치우는된다

class UIManager 
{ 
    std::vector<std::function<void()>> d_cleaners; 
    UIManager(UIManager const&) = delete; 
    void operator=(UIManager const&) = delete; 
public: 
    UIManager(); 
    ~UIManager(); 

    template <typename T> 
    T *getUI() { 
     static std::map<UIManager const*, T> uis; 
     typename std::map<UIManager const*, T>::iterator it = uis.find(this); 
     if (it == uis.end()) { 
      it = uis.insert(std::make_pair(this, T())).first; 
      this->d_cleaner.push_back([it, &uis](){ uis.erase(it); }); 
     } 
     return &(it->second); 
    } 
}; 

getUI() 함수는 대응하는 오브젝트에 UIManager의 어드레스 매핑 맵, 즉 this를 저장한다. 그러한 매핑이 없으면 새로운 매핑이 삽입됩니다.또한 오브젝트가 정리되도록 클리너 함수는 해당 맵에서 방금 얻은 반복자에 으로 간단하게 erase()으로 등록됩니다. 코드는 테스트되지 않았지만 해당 라인을 따라 뭔가가 작동합니다.

+0

UI 개체가 다형성 인 경우 일반적인 관례대로 시각적 정리를 수행하는 가상 함수가 있어야합니다. (소멸자가 수행하지만 예외적이어야합니다.) – Potatoswatter

+0

@Potatoswatter : 요점은 무엇인지 이해할 수 없습니다! 그렇습니다, 일은 다르게 행해질 수 있습니다 그러나 그것은 요점 외에있는 것처럼 보입니다. 'getUI ()'를 사용하여 올바른 유형을 얻는 것이 확실한 이점이 있습니다. 멤버 함수를 직접 호출 할 수 있습니다. –

+0

가상 디스패치를 ​​피하는 이점은 성능이지만이 컨텍스트에서는 계산에 포함되지 않습니다. – Potatoswatter