я читал Комментарии на Гуру Саттера Херб Недели редукции о virtual
функции, и, наконец, увидел его упоминание этого:
[…] «использование финала реже» — ну, вроде как. Я не знаю многих, и во время стандартизации Бьярне неоднократно спрашивал примеры проблем, которые он решал, и образцы, где его следует использовать, и я не припоминаю каких-либо основных, которые выделялись. Единственное, что я знаю, это то, что если вы определяете библиотечный модуль (который еще не является стандартной концепцией), то окончательный вариант листовых классов может дать компилятору больше информации для девиртуализации вызовов из-за знания кода вне выигранной библиотеки. Я не знаю, насколько это важно в наши дни при наличии оптимизации всей программы, включая агрессивную девиртуализацию.
Этот ответ не дает много примеров использования вариантов для final
на занятиях, и мне было бы интересно узнать, какие проблемы он действительно может решить. Вы знаете, или будете final
на занятиях становиться только какой-то малоизвестной и почти неиспользованной функцией?
Один интересный необычный случай использования, который я нашел, я описал Вот. Короче говоря, предотвращая наследование от вашего int-like класса, вы покупаете себе возможность заменить его встроенным типом в будущих выпусках вашей библиотеки без риска нарушения кода вашего пользователя.
Но более распространенный пример Реал. Если вы отметите ваш класс как финальный, компилятор может применить определенные оптимизации во время выполнения. Например,
struct Object {
virtual void run() = 0;
virtual ~Object() {}
};
struct Impl final : Object
{
void run() override {}
};
void fun(Impl & i)
{
i.run(); // inlined!
}
Призыв к i.run()
может быть теперь встроен из-за final
спецификатор. Компилятор знает, что просмотр vtable не понадобится.
final
может быть полезно, когда вы предоставляете (своего рода) фасад начальному интерфейсу, который легче использовать подклассам.
Рассматривать:
class IMovable {
public:
void GoTo(unsigned position) = 0;
}
class Stepper : public IMovable {
public:
void GoTo(unsigned position) final;
protected:
virtual void MoveLeft() = 0;
virtual void MoveRight() = 0;
}
void Stepper::GoTo(unsigned position) {
for(;current_pos < position; current_pos++) {
MoveRight();
}
for(;current_pos > position; current_pos--) {
MoveLeft();
}
}
Теперь, если вы хотите получить от Stepper, вы увидите, что вы должны переопределить MoveRight
а также MoveLeft
, но вы не должны переопределять GoTo
,
Это очевидно на этом небольшом примере, но если IMovable
у него было 20 методов, а у Степпера было 25, и были реализации по умолчанию, чем вам было бы трудно понять, что вам следует, а что не следует переопределять. Я встречался с такой ситуацией в библиотеке, связанной с аппаратным обеспечением. Но я бы не назвал это серьезной проблемой, достойной внимания стандартом;)