Спецификатор final для функции

304
01 марта 2017, 21:54

При добавлении спецификатора final мы запрещаем переопределять метод в базовом классе. Тогда зачем нам нужен в базовом классе создавать виртуальный метод, если можно его оставить обычным? Для чего еще используется виртуальный модификатор?

class Base
{
public:
    virtual void doSomething(int x) final;
};

Например вот так:

class Base
{
public:
    void doSomething(int x);
};
Answer 1

Такая комбинация virtual и final будет формально корректной, но фактически бессмысленной. Так что вопрос тут скорее к автору кода: зачем они объявили такой метод?

Если задаться целью попритягивать за уши оправдания для такого использования, то возможной причиной может быть желание формально сделать класс полиморфным. (Например, захотелось мне для каких-то моих целей, чтобы dynamic_cast<void *> работал с указателями типа Base *, а класс Base * как назло у меня не является полиморфным.) Для этого надо создать в нем хотя бы один виртуальный метод, даже если виртуальность этого метода на самом деле "никому больше не нужна". Традиционно в таких ситуациях виртуальным делают деструктор. Но в качестве странноватого альтернативного варианта можно рассмотреть и такую "фиктивную" виртуальность, как в вашем примере.

P.S. Не надо называть невиртуальные методы "статическими". Термин статический метод в языке С++ уже зарезервирован и используется для совсем других целей.

Answer 2

Стандартом c++11 спецификатор final определяется как virt-specifier, поэтому он не имеет смысла для невиртуальных методов и генерирует ошибку:

marked final, but is not virtual

Однако, как правило, final не используется в определении виртуальных методов базового класса.
final используется в классах-наследниках, которые переопределяют метод, чтобы предотвратить дальнейшее изменение реализации метода.
Поэтому final зачастую используется вместе со спецификатором override.

Answer 3

Спецификатор final обычно не используют в базовом классе (смотри ответ vp_arth).

Данный модификатор используется, чтобы запретить переопределение виртуальной функции в классе-наследнике, поэтому обычно используется при наследовании более чем на 2 уровня.

struct A {
    virtual void foo() { std::cout << "I'm A!\n"; }
};
struct B : A {
    virtual void foo() override final { std::cout << "I'm B!\n"; }
};
struct C : B {
    virtual void foo() override { std::cout << "I'm C!\n"; } // <- запрещено, ошибка времени компиляции
};

P.S. В наследниках ключевое слово virtual не несёт смысловой нагрузки, но для наглядности оставил.

READ ALSO
Печать двух страниц на одном листе

Печать двух страниц на одном листе

Как мне в Qt5 настроить так чтобы на одном листе A4 печатать два формата A5?

252
Javascript выпадающий список [требует правки]

Javascript выпадающий список [требует правки]

Как скрывать значение выпадающего спписока

304
Разница ParseFloat и Number [дубликат]

Разница ParseFloat и Number [дубликат]

На данный вопрос уже ответили:

334
Почему выдает такие ошибки в кодах js? [требует правки]

Почему выдает такие ошибки в кодах js? [требует правки]

Добавил файл (minifyjs),и выдает такие ошибки

268