Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I avoid dynamic_cast in my C++ code?

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:

  1. All Cars have an Engine. Further, a FooCar will only ever use a FooEngine.
  2. There are data and algorithms shared by all Engines that I'd rather not copy and paste.
  3. I might want to write a function that requires an Engine to know about its Car.

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:

  1. Have Car provide a pure virtual getEngine() function. That would allow FooCar and BarCar to have implementations that return the correct kind of Engine.
  2. Absorb all of the Engine functionality into the Car inheritance tree. Engine was broken out for maintenance reasons (to keep the Engine 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?

like image 635
Michael Kristofik Avatar asked Jan 19 '09 22:01

Michael Kristofik


2 Answers

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.

like image 195
Drew Dormann Avatar answered Sep 27 '22 18:09

Drew Dormann


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.

like image 25
cletus Avatar answered Sep 27 '22 18:09

cletus