Когда метод объявляется как virtual
в классе автоматически рассматривают его переопределения в производных классах virtual
также, и язык C++ делает это ключевое слово virtual
дополнительный в этом случае:
class Base {
virtual void f();
};
class Derived : public Base {
void f(); // 'virtual' is optional but implied.
};
Мой вопрос: Каково объяснение для того, чтобы сделать virtual
дополнительный?
Я знаю, что это не абсолютно необходимо, чтобы компилятор был сказан, что, но я буду думать, что разработчики извлекли бы выгоду, если бы такое ограничение было осуществлено компилятором.
Например, иногда когда я читал код других, интересно, является ли метод виртуальным, и я должен разыскать его суперклассы для определения этого. И некоторые стандарты кодирования (Google) делают это 'должен' для помещения virtual
ключевое слово во всех подклассах.
Да, в этом случае было бы лучше заставить компилятор принудительно применять виртуальное, и я согласен с тем, что это ошибка дизайна, которая поддерживается для обратной совместимости.
Однако есть один трюк, который без него был бы невозможен:
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 везде, где это возможно.
В качестве сопутствующего примечания, в 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()
и в итоге ввели совершенно новую функцию, когда хотели переопределить существующую.
Слабое место в дизайне, согласен. Я также думаю, что было бы действительно , если бы был другой синтаксис для двух разных вещей:
С текущими правилами C ++ при переопределении функции - легко напортачить. Если вы неправильно набрали имя функции (или допустили ошибку в списке ее параметров) - тогда вы действительно набираете (1) вместо (2).
И у вас нет ошибок / предупреждений. Просто получите сюрприз во время выполнения.
Поскольку язык не может обеспечить "хороший" стиль, C ++ обычно даже не пытается этого сделать. По крайней мере, ИМО, остается открытым вопрос, является ли включение таких избыточных спецификаторов хорошим стилем в любом случае (лично я ненавижу, когда они там есть).
(По крайней мере частично) стандарты кодирования Google могут иметь смысл при некоторых обстоятельствах, но что касается C ++ в целом, они обычно считаются в лучшем случае посредственными советами. В некоторой степени они даже признают это - некоторые из них, о которых они открыто заявляют, действительно существуют только для того, чтобы соответствовать их старому коду. Другие части они не признают так прямо, и (если быть полностью честным) этот аргумент в любом случае не поддерживает некоторые из их стандартов (т.е. некоторые из них, похоже, не имеют реального обоснования).
Это хороший вопрос, и я, конечно, согласен с тем, что переобъявление виртуального метода в производном классе является хорошим стилем, если он был объявлен виртуальным в базовом классе. Хотя есть некоторые языки, которые создают стиль в языке (например, Google Go и, в некоторой степени, Python), C ++ не является одним из этих языков.Хотя компилятор, безусловно, может обнаружить, что производный класс не использует повторно ключевое слово «виртуальный» для чего-то, объявленного «виртуальным» в базовом классе (или, что более важно, что производный класс объявляет функцию с тем же именем, что и базовый класс, и он не был объявлен виртуальным в базовом классе), на самом деле во многих компиляторах есть настройки для выдачи предупреждений (и даже сообщений об ошибках) в случае, если это произойдет. Однако на данном этапе было бы нецелесообразно вводить такое требование в языке, поскольку существует слишком много кода, который не является таким строгим. Более того, разработчики всегда могут выбрать более строгие, чем язык, и могут включить уровни предупреждений компилятора.