我如何可以避免dynamic_cast在我的C++编码吗?
-
19-08-2019 - |
题
让我们说有以下类结构:
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
有一个sEngine
.另外,FooCar
将只使用一个FooEngine
. - 有数据和共享的所有算法
Engine
s我不想抄和粘贴。 - 我可能要写一职能,需要一个
Engine
要知道关于它的Car
.
只要我输入 dynamic_cast
当编写这些代码,我知道我可能做错了什么。有没有更好的方式做到这一点?
更新:
基于给出的答案迄今为止,我倾向于两种可能性:
- 已
Car
提供一个纯粹的虚拟getEngine()
功能。这将允许FooCar
和BarCar
有实现,返回正确的那种Engine
. - 吸收所有的
Engine
功能进入Car
继承树。Engine
被打破了对于维护的原因(保留Engine
东西在一个单独的地方)。这是一个贸易之间具有更多的小类(小行代码)对具有较少的大型类。
是否有一个强大的社区的偏好的一些解决方案?是不是有第三个选择我还没有考虑?
解决方案
我假设,汽车拥有一个引擎指针,这就是为什么你发现自己向下转换.
把指针指出的基类,取而代之的是一个纯粹的虚拟get_engine()function.然后你FooCar和BarCar可持指向正确的动机类型。
(编辑)
为什么这个工作:
由于虚拟功能 Car::get_engine()
将返回 基准或指, C++将允许派生的课程来实现这个功能 不同的回报类型, ,只要返回的类型不同于通过更多的源类型。
这就是所谓 协变回归类型, ,并将允许每 Car
类型返回正确的 Engine
.
其他提示
只有一件事我想添加:该设计已经味道不好我因为什么我呼吁 平行的树木.
基本上如果你结束了平行的类层次结构(如你所有的汽车,并引擎)然后你只是自寻烦恼。
我会重新考虑如果发动机(甚至汽车)需要有亚类或那些都只是不同情况下的各自相同的基础课程。
你也可以templatize的发动机型号如下
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在FooCar,BarEngine在BarCar
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
s,我会使用 virtual Engine* getEngine()
方法,而不是保持一个指在基类。
如果它的逻辑是具体到 Car
s,我宁愿把共同的 Engine
数据/逻辑在一个单独的目的(不一定是多形态)以及保持 FooEngine
和 BarEngine
执行在他们各自的 Car
孩子类。
当执行回收利用更多的必要界面的继承,目的组成往往提供更大的灵活性。
微软的COM是种笨重的,但它不会有一个新的概念-如果你有一个指向一个接口的一个目的,你可以查询其看如果它支持任何其他接口的使用 QueryInterface 功能。这个想法是要打断你的动机类成多个接口,以便每个可独立使用。
允许我没有错过的东西,这应该是相当微不足道的。
最好的方式来做到这一点是创造纯粹的虚拟的功能的引擎,然后需要在得出类别将实例化。
一个额外的信贷方案可能有一个界面称为IEngine,你而不是passs到你的车,每一个功能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)
{}
};