簡體   English   中英

將基類的指針轉換為繼承的類

[英]Converting a pointer for a base class into an inherited class

我正在開發一個小型的roguelike游戲,對於任何不屬於地圖的對象/“東西”都是基於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指針返回,但它創建了無法解析的循環依賴項。

我對這個很難過。 任何幫助是極大的贊賞。

如果您知道 XEntity是實際工作和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.
}

但是我認為你可以退一步看看程序的設計,因為向下傾斜是應該並且可以通過適當的面向對象設計來避免的。 一個強大的,即使有點復雜的工具可能是訪客模式

我曾經相信,通過“適當”的設計,始終可以避免向下傾斜。 但事實並非如此。 正確的設計通常需要具有實現新的,而不僅僅是不同行為的子對象。 過於頻繁地提倡“正確”設計會告訴您將新行為從抽象堆棧移到不屬於它的地方。 並非總是如此,但是如果你一直在努力確保所有的課程都可以從最抽象的角度使用,那么這往往是事情最終會發生的事情,而且它只是非常丑陋。

以集中方式處理向下轉換的一種好方法是使用訪問者模式。 雖然有幾種形式的訪問者,有些需要向下轉發,有些則不需要。 非循環訪問者,確實需要向下轉換的訪問者,更容易使用,並且根據我的經驗,更強大。

我沒有嘗試過的另一位訪客聲稱以標准訪客的速度滿足非循環訪客的相同靈活性; 它被稱為“合作訪客”。 它仍然可以使用它自己的查找表以更快的方式進行轉換。 我沒有嘗試合作訪問者的原因是我沒有找到一種方法讓它在多個高層建築上工作......但是我沒有花很多時間在它上面因為我已經陷入困境(在我的當前項目)與非循環。

合作訪問者真正酷的是返回類型。 但是,我使用訪問者訪問整個對象塊並使用它們進行操作。 我無法想象返回在這些情況下如何工作。

標准的訪客向下傾斜也是通過虛擬調用機制實現的,這種機制比顯式投射更快,有時更安全。 我不喜歡這個訪問者的事情是,如果你需要訪問Widget高層次的WidgetX,那么即使你不關心它們,你也必須為WidgetY和WidgetZ實現visit()功能。 對於大型和/或廣泛的高層,這可以是PITA。 其他選項不需要這個。

還有一個“高層訪客”。 它知道什么時候退出。

如果您不傾向於使用訪問者並且希望只是強制轉換,那么您可以考慮使用boost :: polymorphic_downcast函數。 它具有動態轉換的安全和警告機制,在調試版本中具有斷言,以及在發布中靜態轉換的速度。 但可能沒有必要。 有時候你只知道自己是正確的。

您需要考慮的重要事項以及您希望避免的是打破LSP。 如果你有很多代碼用“if(widget-> type()== type1){downcast ...} else if(widget-> type()== type2)...”然后添加新的小部件類型是一個很大的問題,會以一種糟糕的方式影響很多代碼。 你的新小部件不會真的是一個小部件,因為你的所有客戶都與你的高層次太親密而且不知道它。 訪問者模式並沒有擺脫這個問題,但它確實集中在一起,這在你聞到難聞的氣味時非常重要,並且它常常使得處理它更加簡單。

XItem * Item = dynamic_cast< XItem * >( Ent );

if ( Item )
    // do something with item

為了使其工作,您需要啟用RTTI。 在這里查看更多信息。

剛剛施展它:

XItem* Item = (XItem*)Ent;

總體而言,更好的方法是:

if (XItem *Item = dynamic_cast<XItem*>(Ent)) {
    Ent->RemoveEntity();

    // Code to link into inventory is here

    break;
}

已經回答,有2個運營商:

XItem* Item = static_cast<XItem*>(Ent);

和:

XItem* Item = dynamic_cast<XItem*>(Ent);

第二個是更慢但更安全(它檢查是否可能)並且即使Ent不是,也可能返回null。

我傾向於使用兩種方法包裝:

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
}

這樣我就可以在開發過程中進行類型檢查(如果出現問題則會出現異常),並且在完成時我會獲得速度。 如果你願意,你可以使用其他策略,但我必須承認我非常喜歡這種方式:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM