Подтвердить что ты не робот

Преувеличивает ли разрушение цель полиморфизма?

Сегодня я столкнулся с вопросом,

4b9b3361

Ответ 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();

Что это.