[英]How can I avoid dynamic_cast in my C++ code?
假設我有以下類結構:
class Car;
class FooCar : public Car;
class BarCar : public Car;
class Engine;
class FooEngine : public Engine;
class BarEngine : public Engine;
讓我們給一輛Car
一個Engine
的手柄。 一個FooCar
將與創建FooEngine*
和BarCar
將與創建BarEngine*
。 有沒有辦法安排事情,所以FooCar
對象可以調用FooEngine
成員函數而無需向下轉換?
這就是為什么類結構按照現在的方式布局的原因:
Car
都有一台Engine
。 此外, FooCar
只會使用FooEngine
。 Engine
都共享數據和算法,我寧願不復制和粘貼。 Engine
知道它的Car
的函數。 一旦我在編寫代碼時鍵入dynamic_cast
,我就知道我可能做錯了什么。 有一個更好的方法嗎?
更新:
根據目前給出的答案,我傾向於兩種可能性:
Car
提供純虛擬的getEngine()
函數。 這將允許FooCar
和BarCar
具有返回正確類型的Engine
。 Engine
功能吸收到Car
繼承樹中。 由於維護原因, Engine
被分解(將Engine
東西保持在一個單獨的位置)。 這是在擁有更多小類(代碼行數較少)與較少大類之間的權衡。 是否有強烈的社區偏好這些解決方案之一? 我還沒有考慮過第三種選擇嗎?
我假設Car擁有一個引擎指針,這就是為什么你會發現自己的低迷。
將指針從基類中取出,並將其替換為純虛擬get_engine()函數。 然后你的FooCar和BarCar可以保持指向正確引擎類型的指針。
(編輯)
為什么這樣有效:
由於虛函數Car::get_engine()
將返回引用或指針 ,因此C ++將允許派生類使用不同的返回類型實現此函數,只要返回類型僅因更多派生類型而不同。
這稱為協變返回類型 ,並允許每種Car
類型返回正確的Engine
。
我只想補充一點:這個設計對我來說已經聞起來很糟糕,因為我稱之為並行樹 。
基本上,如果您最終得到並行類層次結構(就像您使用Car和Engine一樣),那么您只是在尋找麻煩。
如果Engine(甚至是Car)需要有子類,或者那些只是相同的基類的不同實例,我會重新考慮。
您還可以按如下方式對引擎類型進行模板化
template<class EngineType>
class Car
{
protected:
EngineType* getEngine() {return pEngine;}
private:
EngineType* pEngine;
};
class FooCar : public Car<FooEngine>
class BarCar : public Car<BarEngine>
我不明白為什么汽車不能由發動機組成(如果BarCar總是包含BarEngine)。 發動機與汽車有很強的關系。 我會比較喜歡:
class BarCar:public Car
{
//.....
private:
BarEngine engine;
}
FooCar可以使用BarEngine嗎?
如果沒有,您可能希望使用AbstractFactory使用正確的引擎創建正確的汽車對象。
您可以將FooEngine存儲在BarCar中的FooCar,BarEngine中
class Car {
public:
...
virtual Engine* getEngine() = 0;
// maybe add const-variant
};
class FooCar : public Car
{
FooEngine* engine;
public:
FooCar(FooEngine* e) : engine(e) {}
FooEngine* getEngine() { return engine; }
};
// BarCar similarly
這種方法的問題在於獲取引擎是一個虛擬調用(如果你擔心的話),並且在Car
設置引擎的方法需要向下轉換。
我認為這取決於Engine
是否僅由Car
及其子女私下使用,或者您是否也想在其他對象中使用它。
如果Engine
功能不是特定於Car
,我會使用virtual Engine* getEngine()
方法而不是在基類中保留指針。
如果它的邏輯特定於Car
,我寧願將公共Engine
數據/邏輯放在一個單獨的對象中(不一定是多態的),並將FooEngine
和BarEngine
實現保存在各自的Car
子類中。
當實現回收比接口繼承更需要時,對象組合通常提供更大的靈活性。
微軟的COM有點笨重,但確實有一個新穎的概念 - 如果你有一個指向對象接口的指針,你可以使用QueryInterface函數查詢它是否支持任何其他接口。 我們的想法是將您的Engine類分解為多個接口,以便每個接口都可以獨立使用。
允許我沒有錯過任何東西,這應該是相當微不足道的。
執行此操作的最佳方法是在Engine中創建純虛函數,然后在要實例化的派生類中需要這些函數。
一個額外的信用解決方案可能是擁有一個名為IEngine的接口,您可以將其傳遞給您的Car,並且IEngine中的每個功能都是純虛擬的。 你可以有一個'BaseEngine'來實現你想要的一些功能(即'共享')然后從中獲得葉子。
如果你有想要“看起來”像引擎的東西,但可能不是(即模擬測試類等),界面是很好的。
有沒有辦法安排事情,所以FooCar對象可以調用FooEngine的成員函數而無需向下轉換?
像這樣:
class Car
{
Engine* m_engine;
protected:
Car(Engine* engine)
: m_engine(engine)
{}
};
class FooCar : public Car
{
FooEngine* m_fooEngine;
public:
FooCar(FooEngine* fooEngine)
: base(fooEngine)
, m_fooEngine(fooEngine)
{}
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.