私はそのように定義されたテンプレートクラスを持っています:
template <typename T> void ProxyNoOp(T *) {}
template <typename T> void ProxyDelete(T * ptrT) {delete ptrT;}
template <typename T, typename C, void (* Release)(T *) = ProxyNoOp<T> >
class Proxy
{
public:
class Container : public std::list<T *>
{
public:
Container() {}
~Container() {clear();}
void clear()
{
iterator clsEnd = end();
for (iterator clsCurrent = begin(); clsCurrent != clsEnd; ++clsCurrent)
{
T * ptrT = *clsCurrent;
static_cast<Proxy *>(ptrT)->m_ptrContainer = NULL;
Release(ptrT);
}
}
};
private:
iterator m_clsPosition;
C * m_ptrContainer;
public:
Proxy() : m_ptrContainer(NULL) {}
~Proxy()
{
if ( m_ptrContainer != NULL )
{
Container * ptrContainer = static_cast<Container *>(m_ptrContainer);
static_cast<std::list<T *> >(ptrContainer)->erase(m_clsPosition);
}
}
C * GetContainer() const {return m_ptrContainer;}
};
簡潔にするために、多くのコードが削除または変更されています。データ構造の考え方は、コンテナーが含まれる要素への参照を維持するだけでなく、含まれる要素がコンテナーへの参照とコンテナー内の位置を維持し、削除を迅速 (一定時間) かつ自動 (デストラクタで呼び出される) にすることです。 )
このアプローチの問題は、次のコードで示すことができます。
#include "proxy.h" // the above template class
class Child;
class Parent : public Proxy<Child, Parent>::Container {};
class Child : public Proxy<Child, Parent> {};
をコンパイルするParent
と、 の定義によりProxy<Child, Parent>::Container
次の行でエラーが発生します: static_cast<Proxy *>(ptrT)->m_ptrContainer = NULL;
- コンパイルの時点では、Child
定義されておらず、宣言されているだけです。Parent
andの宣言順序を変更すると、次のChild
ようになります。
#include "proxy.h"
class Parent;
class Child : public Proxy<Child, Parent> {};
class Parent : public Proxy<Child, Parent>::Container {};
の定義Proxy<Child, Parent>
により、この行でエラーが発生します:Container * ptrContainer = static_cast<Container *>(m_ptrContainer);
の理由は前と同じです - コンパイルのその時点では、Parent
完全に定義されていません。
これを回避し、引き続きテンプレートを使用する方法はありますか? これを達成するSTL、BOOSTなどの標準データ構造はありますか?
編集:
コードはそのままではコンパイルされません。それについてもっと明確にする必要がありました (上から引用すると、簡潔にするために多くのコードが削除または変更されています。 )。実際には使用しませんstd::list<T *>
が、効果的に同じであるカスタムの二重にリンクされています。この 1 つの問題について尋ねるだけで、大量の補助コードを投稿したくありませんでした。
私はデータ構造自体についての批評を求めていたわけではありません (聞いても構いません) が、なぜここに作成されたのか疑問に思っている人のために、いくつかの例を示します。
ゲーム エンジンには、1 つの戦車モデルとそこから作成された任意の数の戦車がある戦争ゲームなど、モデルとそれらのモデルのインスタンスがあります。クラスは次のように記述できます。
class Instance;
class Model : public Proxy<Model, Instance, ProxyDelete<Instance> >::Container {...};
class Instance : public Proxy<Model, Instance, ProxyDelete<Instance> > {...};
モデルが削除されると、そのモデルから作成されたすべてのインスタンスも削除する必要があります (したがって、ProxyDelete<Instance>
テンプレート パラメーター)。これは、インスタンスが作成元のモデルなしで存在する意味がないためです。
グラフィカル ユーザー インターフェイス (GUI) レンダラーがすべての可視要素 (ボタン、フレームなど) を追跡しているため、何をレンダリングするかを認識していますが、グラフィックス API への別の呼び出しであるため、テキストは別の方法でレンダリングされるとします。
class GuiTextElement;
class GuiRenderer : public Proxy<GuiRenderer, GuiTextElement, ProxyNoOp<GuiTextElement> >::Container {...};
class GuiTextElement : public Proxy<GuiRenderer, GuiTextElement, ProxyNoOp<GuiTextElement> > {...};
std::list<Instance *>
あまり鋭敏でない人は、「単純にのメンバーとして、Model
およびのメンバーとして追加してみませんか?」と言うstd::list<GuiTextElement *>
でしょGuiRenderer
う。いくつかの理由を次に示します。
- コンテナーによって「所有」されている要素は、コンテナーが破棄されても自動的に削除されません (はい、私は知っています
boost::ptr_list
)。 - 別のクラス メンバーを追加しない限り、含まれている要素を使用してコンテナーを参照する方法はありません。たとえば、 から を取得するに
Model
は、Instance
. - #2 を達成するためにクラス メンバーを追加した場合は、要素がコンテナーから削除されたときにも更新する必要があります。
- 速度の狂信者の場合 - 含まれる要素がコンテナ内の位置に反復子を保持していない場合、その要素を削除する前に、まずコンテナを検索する必要があります。