C ++ 11 позволит помечать классы и виртуальный метод как final , чтобы запретить производные от них или отменять их.
class Driver {
virtual void print() const;
};
class KeyboardDriver : public Driver {
void print(int) const final;
};
class MouseDriver final : public Driver {
void print(int) const;
};
class Data final {
int values_;
};
Это очень полезно, потому что оно сообщает читателю интерфейса кое-что о цели использования этого класса / метода. То, что пользователь получает диагностику, если он пытается переопределить, тоже может быть полезно.
Но есть ли преимущество с точки зрения компилятора? Может ли компилятор сделать что-нибудь по-другому, если он знает, что «этот класс никогда не будет производным от» или «эта виртуальная функция никогда не будет переопределена»?
Для final
я в основном нашел только N2751, относящийся к нему. Просматривая некоторые из обсуждений, я обнаружил аргументы, исходящие от C ++ / CLI, но не нашел четкого намека на то, почему final
может быть полезен для компилятора. Я думаю об этом, потому что я также вижу некоторые недостатки маркировки класса final
: Для модульного тестирования защищенных функций-членов можно получить класс и вставить тестовый код. Иногда эти классы являются хорошими кандидатами на получение оценки final
. В этих случаях этот метод был бы невозможен.
В зависимости от того, как вы на это смотрите, у компилятора есть еще одно преимущество (хотя это преимущество просто перенаправляет пользователя, поэтому, возможно, это не преимущество компилятора): компилятор может избежать выдачи предупреждений для конструкций с неопределенным поведением. вещь, которую можно переопределить.
Например, рассмотрим этот код:
class Base
{
public:
virtual void foo() { }
Base() { }
~Base();
};
void destroy(Base* b)
{
delete b;
}
Многие компиляторы будут выдавать предупреждение для не виртуального деструктора b
, когда наблюдается delete b
. Если бы класс Derived
унаследован от Base
и имел свой собственный деструктор ~Derived
, использование destroy
на динамически размещаемом экземпляре Derived
обычно (в соответствии со спецификацией поведения не определено) вызывает ~Base
, но это не звоните ~Derived
. Таким образом, операции очистки ~Derived
не произойдут, и это может быть плохо (хотя в большинстве случаев, вероятно, не катастрофическим).
Если компилятор знает, что Base
не может быть унаследован, тем не менее, не проблема, что ~Base
не является виртуальным, потому что никакая производная очистка не может быть случайно пропущена. Добавление final
к class Base
дает компилятору информацию, чтобы не выдавать предупреждение.
Я точно знаю, что использование final
таким образом подавит предупреждение с помощью Clang. Я не знаю, выдают ли здесь другие компиляторы предупреждение или учитывают ли они окончательность при определении того, следует ли выдавать предупреждение.