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

Стиль С++: префикс виртуального ключевого слова для переопределенных методов

У меня была дискуссия с моими коллегами относительно того, следует ли префикс переопределенных методов с помощью ключевого слова virtual или только в исходном базовом классе.

Я склонен префикс всех виртуальных методов (т.е. методов, связанных с поиском vtable) с ключевым словом virtual. Мое логическое обоснование трижды:

  • Учитывая, что в С++ отсутствует переопределение ключевое слово, наличие виртуального ключевое слово по меньшей мере уведомляет вас о том, что метод включает поиск и теоретически можно было бы переопределить дальнейшие специализации или могут быть вызываемый через указатель на более высокий базовый класс.

  • Последовательно используя этот стиль означает, что, когда вы видите метод (по крайней мере, внутри нашего кода) без ключевое слово virtual, вы можете изначально предполагают, что это не полученных из базы или специализированных в подклассе.

  • Если через некоторую ошибку виртуальные удалены из IFoo, все дети будут по-прежнему функционировать (CFooSpecialization:: DoBar будет все еще переопределяют CFooBase:: DoBar, а не просто скрывать его).

Аргумент против практики, как я понял, был: "Но этот метод не является виртуальным" (который, я считаю, недействителен и проистекает из непонимания виртуальности) и "Когда я вижу виртуальное ключевое слово, Я ожидаю, что это означает, что кто-то вытекает из этого, и ищите их".

Гипотетические классы могут быть распределены по нескольким файлам, и существует несколько специализаций.

class IFoo {
public:
    virtual void DoBar() = 0;
    void DoBaz();
};

class CFooBase : public IFoo {
public:
    virtual void DoBar(); // Default implementation
    void DoZap();
};


class CFooSpecialization : public CFooBase {
public:
    virtual void DoBar(); // Specialized implementation
};

Стилистически ли вы удалили бы ключевое слово virtual из двух производных классов? Если да, то почему? Что такое мысли о переполнении стека?

4b9b3361

Ответ 1

Я полностью согласен с вашим обоснованием. Это хорошее напоминание о том, что метод будет иметь динамическую семантику отправки при вызове. Аргумент "Этот метод не является виртуальным", который используется вашим коллегой, полностью фиктивный. Он перепутал понятия виртуального и чисто виртуального.

Ответ 2

Функция, когда виртуальная виртуальная виртуальная.

Таким образом, в любом случае, если ключевое слово virtual не используется в последующих классах, это не препятствует переопределению функции/метода "i". Поэтому в одном из проектов, над которым я работал, было следующее руководство, которое мне несколько понравилось:

  • Если функция/метод предполагается переопределить, всегда использовать ключевое слово "virtual". Это особенно true при использовании в интерфейсе/базе классы.
  • Если производный класс должен быть подклассифицированным в дальнейшем объяснением укажите ключевое слово "virtual" для каждого функции/метода, которые могут быть переопределены. С++ 11 используйте ключевое слово 'override'
  • Если функция/метод в производном класс не должен быть снова подклассифицируется, тогда ключевое слово "виртуальный" должен быть прокомментирован что функция/метод был отменен, но нет другие классы, которые его переопределяют еще раз. Это, конечно же, не мешает кто-то от переопределения в производный класс, если класс делается окончательным (не выводимым), но оно указывает, что метод не должен быть переопределены. Пример: /*virtual*/ void guiFocusEvent(); С++ 11, используйте ключевое слово "final" вместе с "переопределением", Пример: void guiFocusEvent() override final;

Ответ 3

Добавление virtual не оказывает существенного влияния в любом случае. Я предпочитаю это, но это действительно субъективная проблема. Однако, если вы уверены, что используете override и sealed ключевые слова в Visual С++, вы значительно улучшите способность ловить ошибки во время компиляции.

Я включаю следующие строки в свой PCH:

#if _MSC_VER >= 1400
#define OVERRIDE override
#define SEALED sealed
#else
#define OVERRIDE
#define SEALED
#endif

Ответ 4

Я бы не хотел использовать какой-либо синтаксис, который компилятор позволит мне опустить. Сказав это, часть дизайна С# (в попытке улучшить работу над С++) заключалась в том, чтобы требовать, чтобы переопределения виртуальных методов были помечены как "переопределенные", и это кажется разумной идеей. Меня беспокоит то, что, поскольку он полностью факультативен, это всего лишь вопрос времени, прежде чем кто-то его опустит, и к тому времени вы привыкли ожидать, что переопределения будут иметь "виртуальные". Может быть, лучше всего жить в рамках ограничений языка.

Ответ 5

Я могу думать об одном недостатке: Когда функция члена класса не переопределяется и вы объявляете ее виртуальной, вы добавляете в виртуальную таблицу необязательную запись для определения этого класса.

Ответ 6

Примечание: Мой ответ касается С++ 03, который некоторые из нас все еще придерживаются. С++ 11 имеет ключевые слова override и final, как @JustinTime предлагает в комментариях, которые, вероятно, следует использовать вместо следующего предложения.

Есть много ответов уже и два противоположных мнения, которые выделяются больше всего. Я хочу объединить то, что @280Z28 упомянул в своем ответе с мнением @StevenSudit и рекомендациями стиля @Abhay.

Я не согласен с @280Z28 и не буду использовать расширения языка Microsoft, если вы не уверены, что будете использовать этот код только в Windows.

Но мне нравятся ключевые слова. Итак, почему бы просто не использовать добавление ключевого слова # define-d для ясности?

#define OVERRIDE
#define SEALED

или

#define OVERRIDE virtual
#define SEALED virtual

Разница заключается в том, что вы принимаете решение о том, что вы хотите совершить в случае, если вы наброски в своем третьем пункте.

3 - Если через некоторую ошибку виртуальные удалены из IFoo, все дети будут продолжать функционировать (CFooSpecialization:: DoBar все равно будет переопределять CFooBase:: DoBar, а не просто скрывать его).

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

Если вы выбрали первый вариант и не хотите добавлять #define, вы можете просто использовать комментарии, например:

/* override */
/* sealed */

И это должно выполняться во всех случаях, когда вам нужна ясность, потому что я не считаю слово virtual достаточно ясным для того, что вы хотите.