окончательные виртуальные функции в C++0x

Читая, что вы можете иметь финальные виртуальные функции в C++0x, я немного запутался. В чем разница, если просто исключить оба модификатора?


person B_old    schedule 22.07.2011    source источник
comment
рассмотрим в примере, если Base унаследован от другого класса, который объявляет исходный базовый метод f.   -  person Damien_The_Unbeliever    schedule 22.07.2011


Ответы (2)


Разница заключается в том, что его использует не база, а производная.

class Base {
    virtual void foo() = 0;
};
class Derived : Base {
    void foo() {} 
    // Still virtual because it's virtual in base- no way to "un-virtual" it

    virtual void foo() final {} 
    // Now un-overridable.
};

Думайте об этом не как о предотвращении переопределений, а о предотвращении «большего» переопределения.

person Puppy    schedule 22.07.2011
comment
Спасибо! Мне было непонятно, что виртуальное проходит через всю иерархию. - person B_old; 22.07.2011

Когда я впервые столкнулся с использованием ключевого слова final в сочетании с virtual в C++, мне было интересно то же самое:

Если объявление метода virtual делает его наследуемым и переопределяемым, а объявление метода final предотвращает переопределение этого метода, не Разве объявление метода оба не образуют противоречия?

Я думаю, что текущий принятый ответ на этот вопрос хорош, но я хотел немного развить его, основываясь на том, что я нашел, глядя на это.

Рассмотрим следующий класс:

class A {
    public:
        void hide_me();
        virtual void override_me();
        virtual void cant_override_me() final;
};

Важно понимать, что все три объявления метода различны и означают разные вещи.

Первое:

    void hide_me();

не является виртуальным и поэтому по определению не может быть переопределен.

Третий:

    virtual void cant_override_me() final;

объявлен как final и поэтому не может быть переопределен, в том числе по определению.

Разница в том, что поскольку hide_me не является виртуальным, его переопределение неприменимо, в то время как cant_override_me можно считать подходящим для переопределения (потому что это virtual,) но он также имеет переопределение disabled из-за модификатора final. Другими словами, переопределение не применяется к методам, которые не объявлены virtual, но применимо к методам virtual, вы просто не можете переопределить их, если они также объявлены final.

Теперь рассмотрим дочерний класс:

class B: public A {
    public:
        void hide_me(); // this hide's A's definition of "hide_me()"; this is not overriding.
        void override_me(); // implicit "virtual"
        //void cant_override_me(); // implicit "virtual"; compilation fails
};

Вы можете переопределить hide_me() для класса B, но это просто перегрузка или скрытие, отсюда и название функции. B по-прежнему может получить доступ к методу hide_me A через A::hide_me(), но кто-то еще, у кого есть ссылка на B, объявленная как B, т.е.:

B *my_b = new B();

должен получить доступ к скрытому теперь A определению hide_me через my_b->A::hide_me().

Вы не можете предоставить переопределение cant_override_me() в B.

В качестве полного примера, вот небольшое переопределение программы, чтобы проиллюстрировать, что происходит:

#include <cstdio>    
class A {
    public:
        inline void hide_me() {
            printf("a hide_me\n");
        }
        virtual void override_me();
        virtual void cant_override_me() final;
};

class B: public A {
    public:
        inline void hide_me() {
            printf("b hide_me\n");
        }
        void override_me();
        inline void foo() {
            A::hide_me();
        }
        // can't override cant_override_me
};

void A::override_me() {
    printf("a override_me\n");
}

void A::cant_override_me() {
    printf("a cant_override_me\n");
}

void B::override_me() {
    printf("b override_me\n");
}

int main (int argc, char *argv[]) {
    A *a = new A();
    A *ab = new B();
    B *b = new B();

    a->hide_me();
    ab->hide_me();
    b->hide_me();
    b->A::hide_me();

    printf("---\n");

    a->override_me();
    ab->override_me();
    b->override_me();
    b->A::override_me();
}

Программа выводит

a hide_me
a hide_me
b hide_me
a hide_me
---
a override_me
b override_me
b override_me
a override_me
person Chris Sprague    schedule 01.07.2016