Почему 'виртуальный' дополнительный для переопределенных методов в производных классах?

Когда метод объявляется как virtual в классе автоматически рассматривают его переопределения в производных классах virtual также, и язык C++ делает это ключевое слово virtual дополнительный в этом случае:

class Base {
    virtual void f();
};
class Derived : public Base {
    void f(); // 'virtual' is optional but implied.
};

Мой вопрос: Каково объяснение для того, чтобы сделать virtual дополнительный?

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

Например, иногда когда я читал код других, интересно, является ли метод виртуальным, и я должен разыскать его суперклассы для определения этого. И некоторые стандарты кодирования (Google) делают это 'должен' для помещения virtual ключевое слово во всех подклассах.

15
задан squelart 3 June 2010 в 07:19
поделиться

5 ответов

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

Однако есть один трюк, который без него был бы невозможен:

class NonVirtualBase {
  void func() {};
};

class VirtualBase {
  virtual void func() = 0;
};

template<typename VirtualChoice>
class CompileTimeVirtualityChoice : public VirtualChoice {
  void func() {}
};

С вышесказанным у нас есть выбор времени компиляции, хотим ли мы виртуальность функции или нет:

CompileTimeVirtualityChoice<VirtualBase> -- func is virtual
CompileTimeVirtualityChoice<NonVirtualBase> -- func is not virtual

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

11
ответ дан 1 December 2019 в 03:24
поделиться

В качестве сопутствующего примечания, в C++0x у вас есть возможность принуждать к явным переопределениям с помощью нового синтаксиса атрибутов.

struct Base {
  virtual void Virtual();
  void NonVirtual();
};

struct Derived [[base_check]] : Base {
  //void Virtual(); //Error; didn't specify that you were overriding
  void Virtual [[override]](); //Not an error
  //void NonVirtual [[override]](); //Error; not virtual in Base
  //virtual void SomeRandomFunction [[override]](); //Error, doesn't exist in Base
};

Вы также можете указать, когда вы собираетесь скрыть член, с помощью атрибута [[hiding]]. Это делает ваш код несколько более многословным, но может отловить множество раздражающих ошибок во время компиляции, например, если вы сделали void Vritual() вместо void Virtual() и в итоге ввели совершенно новую функцию, когда хотели переопределить существующую.

6
ответ дан 1 December 2019 в 03:24
поделиться

Слабое место в дизайне, согласен. Я также думаю, что было бы действительно , если бы был другой синтаксис для двух разных вещей:

  1. Объявление виртуальной функции. Т.е. функция, которая может быть переопределена в производном классе. (Эта штука фактически добавляет новую функцию в таблицу vtable.)
  2. Переопределение виртуальной функции в производном классе.

С текущими правилами C ++ при переопределении функции - легко напортачить. Если вы неправильно набрали имя функции (или допустили ошибку в списке ее параметров) - тогда вы действительно набираете (1) вместо (2).

И у вас нет ошибок / предупреждений. Просто получите сюрприз во время выполнения.

3
ответ дан 1 December 2019 в 03:24
поделиться

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

(По крайней мере частично) стандарты кодирования Google могут иметь смысл при некоторых обстоятельствах, но что касается C ++ в целом, они обычно считаются в лучшем случае посредственными советами. В некоторой степени они даже признают это - некоторые из них, о которых они открыто заявляют, действительно существуют только для того, чтобы соответствовать их старому коду. Другие части они не признают так прямо, и (если быть полностью честным) этот аргумент в любом случае не поддерживает некоторые из их стандартов (т.е. некоторые из них, похоже, не имеют реального обоснования).

2
ответ дан 1 December 2019 в 03:24
поделиться

Это хороший вопрос, и я, конечно, согласен с тем, что переобъявление виртуального метода в производном классе является хорошим стилем, если он был объявлен виртуальным в базовом классе. Хотя есть некоторые языки, которые создают стиль в языке (например, Google Go и, в некоторой степени, Python), C ++ не является одним из этих языков.Хотя компилятор, безусловно, может обнаружить, что производный класс не использует повторно ключевое слово «виртуальный» для чего-то, объявленного «виртуальным» в базовом классе (или, что более важно, что производный класс объявляет функцию с тем же именем, что и базовый класс, и он не был объявлен виртуальным в базовом классе), на самом деле во многих компиляторах есть настройки для выдачи предупреждений (и даже сообщений об ошибках) в случае, если это произойдет. Однако на данном этапе было бы нецелесообразно вводить такое требование в языке, поскольку существует слишком много кода, который не является таким строгим. Более того, разработчики всегда могут выбрать более строгие, чем язык, и могут включить уровни предупреждений компилятора.

0
ответ дан 1 December 2019 в 03:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: