dynamic_cast против предоставления виртуальных функций в родительском классе (C++)

У меня есть родительский класс «база» и другой класс «производный», который наследуется от «базы».

"Производный" имеет 1 метод cH1.

если я сделаю это:

base* b = new derived();

И я хочу иметь возможность сделать это:

b->cH1();

Очевидно, я не могу, и есть 2 решения:

  • Либо объявить сЧ1 чисто виртуальным в базе.
  • или сделать это:

    dynamic_cast<derived*>(b)->cH1();
    

Какая из практик лучше?


person Kam    schedule 14.09.2012    source источник
comment
Пожалуйста, выключите клавишу Caps-Lock; PARENT предполагает, что имя является макросом; parent - это обычное использование.   -  person Pete Becker    schedule 14.09.2012
comment
Я бы сказал, что самой большой проблемой в вашей голове может быть использование слов «родитель» и «ребенок». Это действительно ужасные метафоры для базовых и производных классов: в просторечии дочерний элемент обычно не является ни родителем, ни более конкретным экземпляром родителя. Гораздо лучшая метафора — базовый класс и производный класс.   -  person Kerrek SB    schedule 14.09.2012
comment
@PeteBecker и @KerrekSB: хорошие предложения. Кроме того, я бы предложил изменить cH1 (что-то неясное и странное для общего имени метода) на что-то вроде m1 или method1.   -  person Mr.C64    schedule 14.09.2012
comment
Связанный: stackoverflow.com/questions /7200446/   -  person SSJ_GZ    schedule 14.09.2012


Ответы (3)


Если метод cH1 семантически применим к base, сделайте его методом base. В противном случае оставьте cH1 для derived и используйте dynamic_cast. Я думаю, что ваш выбор должен определяться семантикой ваших классов.

Например, если у вас есть базовый класс Vehicle и производные классы Car, Motorbike и Aircraft, метод, подобный TakeOff(), имеет семантику, совместимую с Aircraft, но не с Car или Motorbike, поэтому вы можете сделать TakeOff() методом Aircraft, а не Vehicle метод.

person Mr.C64    schedule 14.09.2012
comment
Я продолжаю слышать это слово и никогда не понимал его :/ что вы подразумеваете под семантически применимым к базе? - person Kam; 14.09.2012
comment
@Kam: Например, если у вас есть базовый класс Vehicle и производные классы Car, Motorbike и Aircraft, такой метод, как TakeOff(), имеет семантику, совместимую с Aircraft, но не с Car или Motorbike, поэтому вы можете сделать TakeOff() методом Aircraft. , а не метод Vehicle. - person Mr.C64; 14.09.2012

dynamic_cast чище и гибче, но немного медленнее.

Помните, когда вы используете dynamic_cast для проверки возвращаемого указателя на NULL.

person Mark Ransom    schedule 14.09.2012
comment
Как dynamic_cast чище и гибче? Есть случаи, когда dynamic_cast может быть оправдано, но из предоставленной информации я не думаю, что мы можем сделать вывод, что это один из них. (В целом использование виртуальных функций чище.) - person James Kanze; 14.09.2012
comment
@JamesKanze, чище, если родительский класс не загроможден функциями, которые не имеют отношения к этому классу. Я согласен, что нам нужно больше информации о специфике класса, чтобы знать наверняка. - person Mark Ransom; 14.09.2012

Во-первых, чтобы использовать dynamic_cast, base должна иметь хотя бы одну виртуальную функцию.

Во-вторых, использование dynamic_cast обычно является признаком ошибки дизайна. Если derived действительно является потомком base, то объект derived должен иметь возможность стоять там, где ожидается объект base, и это обычно означает, что base имеет виртуальные функции, полностью виртуальные или нет, и что derived переопределяет некоторые или все из них. их.

Однако, не зная, что делает cH1, невозможно рекомендовать подход.

person Pete Becker    schedule 14.09.2012
comment
Это не всегда ошибка — иногда вам нужна коллекция объектов, которые имеют что-то общее, но не имеют ничего общего. - person Mark Ransom; 14.09.2012
comment
Использование dynamic_cast для конкретного класса почти всегда является ошибкой, но класс (или группа классов) нередко поддерживает расширенный интерфейс. В таких случаях dynamic_cast является одним из способов поддержки доступа к расширенному интерфейсу. - person James Kanze; 14.09.2012
comment
Я сделал ударение на слове обычно, так как оно, похоже, было упущено из виду. Я не говорил, что это всегда ошибка. - person Pete Becker; 14.09.2012