継承クラスに基本クラスへのポインタを変換
-
22-09-2019 - |
質問
私は小さなローグライクゲームに取り組んで、そしてマップの一部ではない任意のオブジェクト/「もの」がXEntityクラスをベースにしていためです。このようXPlayer、XItem、およびXMonsterとしてそれに依存するいくつかのクラスは、あります。
私の問題は、私は、オブジェクトが項目であることを知っているときXEntityからXItemへのポインタを変換すること、です。私はアイテムを拾うために使用していますサンプルコードはこれです、それがオーバー立っているアイテムアップ時に異なるエンティティのピックです。
void XEntity::PickupItem()
{
XEntity *Ent = MapList; // Start of a linked list
while(true)
{
if(Ent == NULL) { break; }
if(Ent->Flags & ENT_ITEM)
{
Ent->RemoveEntity(); // Unlink from the map's linked list
XItem *Item = Ent // Problem is here, type-safety
// Code to link into inventory is here
break;
}
Ent = Ent->MapList;
}
}
私が最初に考えたことXEntityにXItemポインタと戻り自体が、それが解決不能である円形の依存関係を作成するメソッドを作成することであった。
私はかなりこの1について困惑しています。すべてのヘルプは大歓迎されます。
解決
XEntity
が、あなたは、静的なキャストを使用することができますactuallとXItem
であること。 このあなたの知っているの場合は
XItem* Item = static_cast<XItem *>(Ent);
しかし、あなたが設計し、あなたが途中でエンティティを操作することができるかどうかを確認確認する必要がありますあなたはそれがあるタイプ派生知る必要がないことを意味しています。あなたは、基本クラスに十分に豊富なインターフェイスを与えることができた場合は、フラグチェックのタイプの検査を解消できる可能性があります。
他のヒント
解き問題をキャスティング
// dynamic_cast validates that the cast is possible. It requires RTTI
// (runtime type identification) to work. It will return NULL if the
// cast is not possible.
XItem* Item = dynamic_cast<XItem*>(Ent);
if(Item)
{
// Do whatever you want with the Item.
}
else
{
// Possibly error handling code as Ent is not an Item.
}
しかし、私はダウンキャストは、適切なオブジェクト指向設計することにより回避することができるべきものであるとして、あなたは、プログラムの設計でステップバックや外観をsouldと思います。一つの強力な、少しでも複雑なのに、ツールがあるかもしれないビジターパターンでます。
私はダウンキャストが「適切」なデザインを避けるために、常に可能であったと信じていました。これはちょうどかかわらず、そうではありません。適切な設計は、非常に多くの場合、新たに実装するサブオブジェクトを持つ必要がある、とだけでなく、異なる振る舞い。あまりにも頻繁に「適切な」設計の提唱、それが属していない場所に抽象化のスタックアップ新しい行動を移動することを教えてくれます。あなたは必ず、すべてのクラスを作るために努力を続ける場合は常にではないが、これは非常に頻繁に物事が起こってしまうと、それだけで非常に醜いだところで最も抽象的なポイントから使用することができます。
一元的にダウンキャストに対処するための一つの素晴らしい方法は、Visitorパターンを使用することです。訪問者のいくつかの形式は、一部にはない、いくつかは、ダウンキャストが必要な、しかしがあります。非環式訪問者、ダウンキャストを必要としないものは、私の経験では、より強力なと作業が容易とされている。
私は、標準的な訪問者の速度と非環式ビジターの同じ柔軟性を満たすために主張して仕事にしようとしていませんでしたことを別の訪問者。それは、「協同組合の訪問者」と呼ばれています。それはまだそれが自分のルックアップテーブルだとそれだけで迅速な方法でそうし、キャストします。私は協力の訪問者を試していない理由は、私はそれが複数higherarchies上で動作させるための方法を見つけていませんでしたということです...しかし、私は自分自身を立ち往生してきたので、私は中に(どちらかそれに多くの時間を費やしていませんでした私非環式で現在のプロジェクト)。
協同組合訪問者に関する本当のクールな事は、戻り値の型です。しかし、私は、オブジェクトのブロック全体を訪問し、彼らと物事を行うために私の訪問者を使用します。私はリターンがこれらのケースでどのように動作するか悩み構想を持っています。
標準ビジターダウンキャストもそれだけで速く、時には明示的なキャストよりも安全である仮想呼び出しメカニズムを通じてそう。私はこの訪問者については好きではない事は、あなたがウィジェットhigherarchyにWidgetXを訪問する必要がある場合、あなたはまた、あなたがそれらを気にしないにもかかわらずWidgetYとWidgetZのための訪問()機能を実装しなければならないということです。大規模および/またはワイドhigherarchiesでは、これは面倒くさすることができます。その他のオプションはこれを必要としません。
「higherarchal訪問者」もあります。それが終了したときに知っています。
あなたは、あなたがブースト:: polymorphic_downcast機能を使用して検討するかもしれないのにと願いだけキャストへの訪問者を使用するように傾斜していない場合。これは、デバッグビルドでアサートし、リリースでは、静的なキャストの速度で動的キャストの安全性および警告のメカニズムを持っています。これは、しかし必要ではないかもしれません。時には、あなたはちょうどあなたが右のキャストしていることを知っています。
あなたが考える必要があることを、あなたが避けたいものを重要なことは、LSPを壊しています。あなたがコードの全体の多くを持っている場合は、「(()== TYPE1タイプwidget->){...}意気消沈他の場合であれば(widget->タイプ()== TYPE2)...」その後、新しいウィジェットタイプを追加悪い方法でコードの多くに影響を与える大きな問題です。すべてのクライアントは、あなたのhigherarchyとあまりにも親密であり、それについて知らないので、あなたの新しいウィジェットは本当にウィジェットではありません。ビジターパターンは、この問題を取り除くしませんが、それはあなたが悪い匂いを持っているときに、非常に重要である一元化を行い、そして、それは多くの場合、それは単純にそれの上に対処できるようになります。
ただ、それをキャストします:
XItem* Item = (XItem*)Ent;
より良いアプローチは、全体的に、これです
if (XItem *Item = dynamic_cast<XItem*>(Ent)) {
Ent->RemoveEntity();
// Code to link into inventory is here
break;
}
XItem * Item = dynamic_cast< XItem * >( Ent );
if ( Item )
// do something with item
仕事にそのためには、あなたはRTTIを有効にする必要があります。ルックここを詳しくます。
のように回答されている、2つの事業者があります:
XItem* Item = static_cast<XItem*>(Ent);
と
XItem* Item = dynamic_cast<XItem*>(Ent);
第二は、(それが可能だ場合にチェック)遅いが、より安全であるとEnt
がない場合でも、ヌルが返されることがあります。
私はこの方法に包まれ、両方を使用する傾向がある。
template <class T, class U>
T* my_cast(U* item)
{
#ifdef _NDEBUG_
if (item) return &dynamic_cast<T&>(*item); // throw std::bad_cast
else return 0;
#else
return static_cast<T*>(item);
#endif
}
(何かが悪くなった場合の例外を除いて)私は、開発中に型チェックを取得し、私は終わっていたとき、私はスピードを得るこの方法です。ご希望の場合は、他の戦略を使用することができますが、私は非常にこの方法のように私を認めなければならない:)