1

単一の UI クラスから継承する一連のクラスを管理する UIManager があります。現在、個々の UI が遅延して初期化され、静的に保存される次のように動作します。

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が強制終了されたときにUIオブジェクトを削除するために上記を書き直すにはどうすればよいですか?

======================================

これが私が実装した解決策です。初期の結果は、うまく機能しているということです。

基本的に、私は 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;
   }
}
4

2 に答える 2

2

現在、各タイプの 1 つの UI 要素に割り当てられているストレージ スペースのみがあります。その原則を守りながらウィンドウの数を増やすことは基本的に不可能です。

手っ取り早い解決策は、ウィンドウ番号のテンプレート引数を追加することです。それがゲームであり、プレーヤーの数が限られている場合は、あらかじめ決められた数のウィンドウ用の静的ストレージを使用できます。

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

ただし、UI ID を型システムに結び付けるアプローチには根本的な欠陥があるため、ポリモーフィズムとコンテナーを使用した従来のアプローチをお勧めします。


オブジェクトをタイプ別に識別しながら動的に保存する 1 つの方法は、次のようになります。

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

これには仮想デストラクタが必要であることに注意してください。そうしUIBaseないと、終了時にオブジェクトが適切に終了しません。

于 2013-09-10T23:06:35.530 に答える
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()マッピングするマップを格納します。そのようなマッピングがない場合は、新しいマッピングが挿入されます。さらに、オブジェクトを確実にクリーンアップするために、対応するマップから取得したばかりのイテレータに、クリーナー関数を登録します。コードはテストされていませんが、これらの行に沿った何かが機能するはずです。UIManagerthisthiserase()

于 2013-09-10T23:18:46.860 に答える