Варианты использования для выпускных классов

я читал Комментарии на Гуру Саттера Херб Недели редукции о virtual функции, и, наконец, увидел его упоминание этого:

[…] «использование финала реже» — ну, вроде как. Я не знаю многих, и во время стандартизации Бьярне неоднократно спрашивал примеры проблем, которые он решал, и образцы, где его следует использовать, и я не припоминаю каких-либо основных, которые выделялись. Единственное, что я знаю, это то, что если вы определяете библиотечный модуль (который еще не является стандартной концепцией), то окончательное создание конечных классов может дать компилятору больше информации для девиртуализации вызовов из-за знания кода вне выигранной библиотеки. Я не знаю, насколько это важно в наши дни при наличии оптимизации всей программы, включая агрессивную девиртуализацию.

Этот ответ не дает много примеров использования вариантов для final на занятиях, и мне было бы интересно узнать, какие проблемы он действительно может решить. Вы знаете, или будете final на занятиях становиться только какой-то малоизвестной и почти неиспользованной функцией?

22

Решение

Один интересный необычный случай использования, который я нашел, я описал Вот. Короче говоря, предотвращая наследование от вашего 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 не понадобится.

9

Другие решения

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, и были реализации по умолчанию, и вам было бы нелегко понять, что вам следует, а что не следует переопределять. Я встречался с такой ситуацией в библиотеке, связанной с аппаратным обеспечением. Но я бы не назвал это серьезной проблемой, достойной внимания стандартом;)

0

По вопросам рекламы [email protected]