How can I avoid dynamic_cast in my C++ code?
-
19-08-2019 - |
Question
Let's say I have the following class structure:
class Car;
class FooCar : public Car;
class BarCar : public Car;
class Engine;
class FooEngine : public Engine;
class BarEngine : public Engine;
Let's also give a Car
a handle to its Engine
. A FooCar
will be created with a FooEngine*
and a BarCar
will be created with a BarEngine*
. Is there a way to arrange things so a FooCar
object can call member functions of FooEngine
without downcasting?
Here's why the class structure is laid out the way it is right now:
- All
Car
s have anEngine
. Further, aFooCar
will only ever use aFooEngine
. - There are data and algorithms shared by all
Engine
s that I'd rather not copy and paste. - I might want to write a function that requires an
Engine
to know about itsCar
.
As soon as I typed dynamic_cast
when writing this code, I knew I was probably doing something wrong. Is there a better way to do this?
UPDATE:
Based on the answers given so far, I'm leaning towards two possibilities:
- Have
Car
provide a pure virtualgetEngine()
function. That would allowFooCar
andBarCar
to have implementations that return the correct kind ofEngine
. - Absorb all of the
Engine
functionality into theCar
inheritance tree.Engine
was broken out for maintenance reasons (to keep theEngine
stuff in a separate place). It's a trade-off between having more small classes (small in lines of code) versus having fewer large classes.
Is there a strong community preference for one of these solutions? Is there a third option I haven't considered?
Solution
I'm assuming that Car holds an Engine pointer, and that's why you find yourself downcasting.
Take the pointer out of your base class and replace it with a pure virtual get_engine() function. Then your FooCar and BarCar can hold pointers to the correct engine type.
(Edit)
Why this works:
Since the virtual function Car::get_engine()
would return a reference or a pointer, C++ will allow derived classes to implement this function with a different return type, as long as the return type only differs by being a more derived type.
This is called covariant return types, and will allow each Car
type to return the correct Engine
.
OTHER TIPS
Just one thing I wanted to add: this design already smells bad to me because of what I call parallel trees.
Basically if you end up with parallel class hierarchies (as you have with Car and Engine) then you're just asking for trouble.
I would rethink if Engine (and even Car) needs to have subclasses or those are all just different instances of the same respective base classes.
You could also templatize the Engine type as follows
template<class EngineType>
class Car
{
protected:
EngineType* getEngine() {return pEngine;}
private:
EngineType* pEngine;
};
class FooCar : public Car<FooEngine>
class BarCar : public Car<BarEngine>
I don't see why a car can't be composed of an engine (if BarCar will always contain BarEngine). Engine has a pretty strong relationship with the car. I would prefer:
class BarCar:public Car
{
//.....
private:
BarEngine engine;
}
Would it be possible for the FooCar to use the BarEngine?
If not, you might want to use an AbstractFactory to create the right car object, with the right engine.
You can store the FooEngine in FooCar, BarEngine in 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
The problem with this approach is that getting an engine is a virtual call (if you're concerned about that), and a method for setting an engine in Car
would require downcasting.
I think that it depends if Engine
is only privately used by Car
and its children or if you also want to use it in other objects.
If Engine
functionalities are not specific to Car
s, I would use a virtual Engine* getEngine()
method instead of keeping a pointer in the base class.
If its logic is specific to Car
s, I would prefer putting the common Engine
data/logic in a separate object (not necessarily polymorphic) and keep FooEngine
and BarEngine
implementation in their respective Car
child class.
When implementation recycling is more needed than interface inheritance, object composition often offer greater flexibility.
Microsoft's COM is kind of clunky, but it does have a novel concept - if you have a pointer to an interface of an object, you can query it to see if it supports any other interfaces using the QueryInterface function. The idea is to break your Engine class into multiple interfaces so that each can be used independently.
Allowing I haven't missed something, this should be rather trivial.
The best way to do this is to create pure virtual functions in Engine, that are then required in derived classes that are to be instantiated.
An extra credit solution would probably be to have an interface called IEngine, that you instead passs to your Car, and every function in IEngine is pure virtual. You could have a 'BaseEngine' that implements some of the functionality you want (i.e., 'shared') and then have leaf ones off of that.
The interface is nice should you ever have something you want to 'look' like an engine, but probably isn't (i.e., mock test classes and such).
Is there a way to arrange things so a FooCar object can call member functions of FooEngine without downcasting?
Like this:
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)
{}
};