Теперь, если есть более одного типа автомобилей, которые могут выдвигаться, скажем, такие автомобили CarA
, CarB
и CarC
(в дополнение к Lamborghini
), то вы собираетесь написать это:
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();
}
Таким образом, лучший дизайн в таких случаях был бы следующим: добавьте интерфейс с именем IRetractable
и извлекайте из него также:
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 {
//...
};
Тогда вы можете просто написать это:
if(IRetractable *retractable = dynamic_cast<IRetractable *>(cars[i]))
{
retractable->retractTheRoof(); //Call polymorphically!
}
Cool? Не правда ли?
Онлайн демо: http://www.ideone.com/1vVId
Конечно, здесь все еще используется dynamic_cast
, но важный момент в том, что вы играете только с интерфейсами , не нужно упоминать конкретный класс где угодно. Другими словами, в дизайне все еще максимально используется полиморфизм времени выполнения . Это один из принципов Design Patterns :
"Запрограммируйте на" интерфейс ", а не" реализацию "." (Банда четырех 1995: 18)
Также смотрите это:
Другим важным моментом является то, что вы должны сделать деструктор Car
(базовый класс) виртуальным:
class Car{
public:
virtual ~Car() {} //important : virtual destructor
virtual int goFast() = 0;
};
Это важно, потому что вы поддерживаете вектор Car*
, это означает, что позже вы захотите удалить экземпляры через указатель базового класса, для чего вам нужно сделать ~Car()
виртуальным деструктором, в противном случае delete car[i]
вызовет неопределенное поведение.