Пустая виртуальная функция: хорошая практика?

#c #oop #polymorphism #virtual-functions

#c #ооп #полиморфизм #виртуальные функции

Вопрос:

У меня есть ученик суперкласса, от которого наследуют младший класс и старший класс.

 virtual void student::attend(void){
        //call either Junior's or Senior's attend
        //if attend is called on a Student object, do nothing.
}
 void Junior::attend(void){
    //do stuff
}
 void Senior::attend(void){
  //do stuff
}
 

По сути, мой виртуальный метод в суперклассе сейчас пуст, все, что он делает, это вызывает правильный старший или младший attend() . Я не хочу, чтобы что-то происходило, если объект вызывается для объекта Student.
Причина, по которой я изучаю это, заключается в том, что я хочу иметь что-то вроде std::vector <student*> studv where studv будет содержать оба указателя на Senior и Junior и вызывать attend() для каждого объекта вектора без необходимости указывать что-либо. Итак, у меня есть два вопроса:

а) Считается ли это хорошей практикой? Есть ли лучшая альтернатива? б) Учитывая, что один из моих классов, наследующих суперкласс, может никогда не использовать одну из его функций, что мне делать? Должен ли я удалить его в производном классе или просто ничего не делать?

Большое спасибо!

Комментарии:

1. На самом деле вы можете определить функцию как чисто виртуальную, если не хотите, чтобы объекты типа student создавались напрямую, требуя, чтобы люди использовали полностью производные типы, такие как Junior и Senior . Это будет выглядеть как virtual void student::attend() = 0; в его объявлении.

2. Скорее всего, это будет закрыто как основанное на мнениях. С учетом сказанного, мое мнение заключается в том, чтобы сделать Student абстракцию, используя чисто виртуальные функции. Мое продолжение — включить NVI, или невиртуальный интерфейс, что означает, что ваш общедоступный раздел не будет содержать никаких виртуальных функций, а обычные функции, которые могут в конечном итоге просто вызывать их виртуальный (и теперь защищенный аналог). В зависимости от класса это может быть необязательно, но это может позволить базовому классу выполнить некоторые проверки работоспособности или специализированное поведение перед вызовом виртуальной функции, специфичной для класса.

3. Добавляя немного к тому, что Суиниш сказал о NVI, для небольших программ это может показаться шаблонной суетой. Но по мере роста программы разделение общедоступного API от API, ориентированного на наследование, огромно, особенно для расширяемости и отладки.

4. @NathanPierson Я совершенно забыл об этой возможности, это то, что я думаю, что в конечном итоге сделаю. Также @sweenish, чтобы убедиться, что я правильно понимаю, вы имеете в виду наличие другого метода в абстрактном классе, который вызывал бы мой теперь защищенный и виртуальный attend() ?

Ответ №1:

Реальный вопрос заключается в том, будет ли student объект когда-либо создан сам по себе (по сравнению с подобъектом Junior or Senior ). Если он не будет создан, рассмотрите возможность объявления функции, которая ничего не делает для того, чтобы a student был чисто виртуальным:

 virtual void attend() = 0;
 

Тогда вы не сможете создать student объект самостоятельно — вы получите ошибку компилятора, если попытаетесь — и, кроме того, это самодокументируется относительно этого факта, и вам также не нужно предоставлять пустую реализацию. Несколько преимуществ.

Комментарии:

1. Я решил, что это будет мой подход (я по какой-то причине забыл об этой возможности), поскольку student не имеет смысла создавать экземпляры напрямую.