簡體   English   中英

如何在C ++代碼中避免使用dynamic_cast?

[英]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成員函數而無需向下轉換?

這就是為什么類結構按照現在的方式布局的原因:

  1. 所有的Car都有一台Engine 此外, FooCar只會使用FooEngine
  2. 所有Engine都共享數據和算法,我寧願不復制和粘貼。
  3. 我可能想編寫一個需要Engine知道它的Car的函數。

一旦我在編寫代碼時鍵入dynamic_cast ,我就知道我可能做錯了什么。 有一個更好的方法嗎?

更新:

根據目前給出的答案,我傾向於兩種可能性:

  1. Car提供純虛擬的getEngine()函數。 這將允許FooCarBarCar具有返回正確類型的Engine
  2. 將所有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數據/邏輯放在一個單獨的對象中(不一定是多態的),並將FooEngineBarEngine實現保存在各自的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.

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