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{
publ
If you care about the retractability of the roof, you could have a ConvertibleCar
abstract base class between Car
and Lamborghini
in the inheritance chain :
class Car {
public :
virtual int goFast() = 0;
};
class ConvertibleCar : public virtual Car {
public :
virtual void retractTheRoof() = 0;
};
class FordFocus : public Car {
public :
int goFast() { return 35; };
};
class Lamborghini : public ConvertibleCar {
bool roof;
public :
int goFast() { return -1/0; /* crash */ };
void retractTheRoof() { roof = 0; };
};
If the RichGuy
class can't keep track of all different kinds of cars separately, you can still use dynamic_cast
to figure out if a certain car is of a certain type :
ConvertibleCar* convertible = dynamic_cast<ConvertibleCar*>(cars[i]);
if (convertible) {
convertible->retractTheRoof();
};
Note that this scales quite well with different car types (ConvertibleCar
, AllTerrainCar
, SportsCar
, ...), where the same car can inherit from 0 or more of these types. A Lamborghini
would probably derive from both ConvertibleCar
and SportsCar
:
class Lamborghini : public SportsCar, public ConvertibleCar {
// ...
};
Now if there are more than one type of cars which is retractable, say such cars are CarA
, CarB
, and CarC
(in addition to Lamborghini
), then are you going to write this:
if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
lambo->retractTheRoof();
}
else if(CarA * pCarA = dynamic_cast<CarA*>(cars[i])) {
pCarA->retractTheRoof();
}
else if(CarB * pCarB = dynamic_cast<CarB*>(cars[i])) {
pCarB->retractTheRoof();
}
else if(CarC * pCarC = dynamic_cast<CarC*>(cars[i])) {
pCarC->retractTheRoof();
}
So a better design in such cases would be this: add an interface called IRetractable
and derive from it as well:
struct IRetractable
{
virtual void retractTheRoof() = 0;
};
class Lamborghini : public Car, public IRetractable {
//...
};
class CarA : public Car, public IRetractable {
//...
};
class CarB : public Car, public IRetractable {
//...
};
class CarC : public Car, public IRetractable {
//...
};
Then you can simply write this:
if(IRetractable *retractable = dynamic_cast<IRetractable *>(cars[i]))
{
retractable->retractTheRoof(); //Call polymorphically!
}
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:
"Program to an 'interface', not an 'implementation'." (Gang of Four 1995:18)
Also, see this:
Other important point is that you must make the destructor of Car
(base class) virtual:
class Car{
public:
virtual ~Car() {} //important : virtual destructor
virtual int goFast() = 0;
};
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, otherwise delete car[i]
would invoke undefined behaviour.
Instead of checking for "Lamborghini" in this module, because I know for a fact that Lamborghini is not the only car maker that makes cars with retractable roofs.
Then you don't need dynamic-casting at all. That's how it should have been done.
class Car{
public:
virtual int goFast() = 0;
virtual void retractTheRoof(){ /*no-op*/}; // default
};
class Lamborghini : public Car {
bool roof;
public:
int goFast(){
return -1/0; // crash
};
void retractTheRoof(){ roof = 0;}; // specific
};
and then in the code instead of
if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
lambo->retractTheRoof();
}
do
cars[i]->retractTheRoof();
That's it.
Yes, this is usually the better design case. You should only introduce a virtual function into the hierarchy if it makes sense for all derived classes.
However, your MODEL enum is completely worthless- that's what dynamic_cast
is actually for.
if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
lambo->retractTheRoof();
}