I encountered a question today, found here, which raised this question for me.
Here’s a pseudo-code example of what I’m getting at:
class Car{
public:
virtual int goFast() = 0;
};
class FordFocus : public Car {
public:
int goFast(){
return 35;
};
};
class Lamborghini : public Car {
bool roof;
public:
int goFast(){
return -1/0; // crash
};
void retractTheRoof(){
roof = 0;
};
};
class RichGuy {
vector<Car *> cars;
public:
void goDrive() {
for(int i = 0; i < cars.size(); ++i) {
if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
lambo->retractTheRoof();
};
goFast();
};
};
};
In the example, there is a RichGuy class. Richguy only keeps track of his Cars in a single vector. Because he has so many Cars it would be too troublesome to keep track of them based on if they’re a FordFocus or a Lamborghini. However, the only type of car he has with a retractable roof is the Lambo. In order to retractTheRoof(), RichGuy must now determine if the Car he has is indeed a Lamboghini, and then downcast to execute this function of it.
Based on this example, was the choice to downcast in good design? Or did it violate the purpose of polymorphism, assuming that purpose is to allow derived classes to define their own behavior, and provide a common interface for classes like RichGuy? And if so, is there a better way to allow for functions like retractTheRoof() (or at least it’s effect) to be available to RichGuy to use?
Now if there are more than one type of cars which is retractable, say such cars are
CarA,CarB, andCarC(in addition toLamborghini), then are you going to write this:So a better design in such cases would be this: add an interface called
IRetractableand derive from it as well:Then you can simply write this:
Cool? Isn’t it?
Online demo : http://www.ideone.com/1vVId
Of course, this still uses
dynamic_cast, but the important point here is that you’re playing with interfaces only, no need to mention concrete class anywhere. In other words, the design still makes use of runtime-polymorphism as much as possible. This is one of the principle of Design Patterns:Also, see this:
Other important point is that you must make the destructor of
Car(base class) virtual:Its imporant because you’re maintaining a vector of
Car*, that means, later on you would like to delete the instances through the base class pointer, for which you need to make~Car()a virtual destructor, otherwisedelete car[i]would invoke undefined behaviour.