Преувеличивает ли разрушение цель полиморфизма?
Ответ 1
Теперь, если есть несколько типов автомобилей, которые убираются, скажем, что такие автомобили 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!
}
Охладить? Не правда ли?
Демо-версия онлайн: http://www.ideone.com/1vVId
Конечно, это все еще использует dynamic_cast
, но важная точка заключается в том, что вы играете только с интерфейсами, не нужно упоминать конкретный класс в любом месте. Другими словами, дизайн по-прежнему использует максимально возможный полиморфизм во время выполнения. Это один из принципов Шаблоны проектирования:
"Программа для" интерфейса ", а не" реализация ". (Gang of Four 1995: 18)
Также см. это:
Другим важным моментом является то, что вы должны сделать деструктор Car
(базовый класс) виртуальным:
class Car{
public:
virtual ~Car() {} //important : virtual destructor
virtual int goFast() = 0;
};
Это важно, потому что вы поддерживаете вектор Car*
, это означает, что позже вы хотели бы удалить экземпляры с помощью указателя базового класса, для чего вам нужно сделать ~Car()
виртуальный деструктор, иначе delete car[i]
будет ссылаться на поведение undefined.
Ответ 2
Да, это, как правило, лучший пример дизайна. Вы должны ввести виртуальную функцию только в иерархию, если она имеет смысл для всех производных классов.
Однако ваше перечисление MODEL совершенно бесполезно, что на самом деле dynamic_cast
.
if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
lambo->retractTheRoof();
}
Ответ 3
Если вам нужна убирающаяся крыша, у вас может быть базовый класс ConvertibleCar
между Car
и Lamborghini
в цепочке наследования:
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; };
};
Если класс RichGuy
не может отслеживать все разные виды автомобилей отдельно, вы все равно можете использовать dynamic_cast
, чтобы выяснить, имеет ли определенный автомобиль определенный тип:
ConvertibleCar* convertible = dynamic_cast<ConvertibleCar*>(cars[i]);
if (convertible) {
convertible->retractTheRoof();
};
Обратите внимание, что это достаточно хорошо подходит для разных типов автомобилей (ConvertibleCar
, AllTerrainCar
, SportsCar
,...), где один и тот же автомобиль может наследовать от 0 или более из этих типов. A Lamborghini
, вероятно, вытекает как из ConvertibleCar
, так и SportsCar
:
class Lamborghini : public SportsCar, public ConvertibleCar {
// ...
};
Ответ 4
Вместо того, чтобы проверять "Lamborghini" в этом модуле, потому что я знаю, что Lamborghini - не единственный производитель автомобилей, который делает автомобили с выдвижными крышами.
Тогда вам вообще не нужен динамический кастинг. То, как это должно было быть сделано.
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
};
а затем в коде вместо
if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
lambo->retractTheRoof();
}
делать
cars[i]->retractTheRoof();
Что это.