Подтвердить что ты не робот

Почему "виртуальность" методов неявно распространяется на С++?

В чем причина удаления возможности остановить распространение методов виртуальности?

Позвольте мне быть яснее: на С++, напишите ли вы "virtual void foo()" или "void foo()" в производном классе, он будет виртуальным до тех пор, пока в базовом классе foo объявляется виртуальным.

Это означает, что вызов foo() через производный * указатель приведет к просмотру виртуальной таблицы (в случае, если функция производного2 переопределяет foo), даже если это поведение не требуется программисту.

Позвольте мне привести вам пример (который выглядит довольно вопиющим для меня) о том, как было бы полезно прекратить распространение виртуальности:

template <class T>
class Iterator // Here is an iterator interface useful for defining iterators
{              // when implementation details need to be hidden
public:
    virtual T& next() { ... }
    ...
};

template <class T>
class Vector
{
public:
    class VectIterator : public Iterator<T>
    {
    public:
        T& next() { ... }
        ...
    };
    ...
};

В приведенном выше примере базовый класс Iterator можно использовать для достижения формы "стирания типа" гораздо более понятным и объектно-ориентированным способом. (См. http://www.artima.com/cppsource/type_erasure.html для примера стирания типа.)

Но все же в моем примере можно напрямую использовать объект Vector:: VectIterator (что будет сделано в большинстве случаев) для доступа к реальному объекту без использования интерфейса.

Если виртуальность не была распространена, вызовы Vector:: VectIterator:: next() даже из указателя или ссылки не были бы виртуальными и могли бы быть встроены и работать эффективно, как если бы интерфейс Iterator не был " t существует.

4b9b3361

Ответ 1

С++ 11 для этой цели добавлено контекстное ключевое слово final.

class VectIterator : public Iterator<T>
{
public:
    T& next() final { ... }
    ...
};

struct Nope : VecIterator {
    T& next() { ... } // ill-formed
};

Ответ 2

Простой ответ: Не смешивайте конкретные и абстрактные интерфейсы! Очевидным подходом в вашем примере будет использование функции virtual virtual, которая делегирует функции virtual, например, do_next(). Производный класс переопределит do_next(), возможно, делегируя функции virtual virtual. Поскольку функции next() вероятны inline, в деле делегирования не требуется никаких затрат.

Ответ 3

По моему мнению, одной из веских причин этого распространения является деструкторы virtual. В С++, когда у вас есть базовый класс с некоторыми методами virtual, вы должны определить деструктор virtual. Это связано с тем, что у некоторого кода может быть указатель базового класса, который фактически указывает на производный класс, а затем пытается удалить этот указатель (подробнее см. question). Определив деструктор в базовом классе как vritual, вы можете убедиться, что все указатели базового класса, указывающие на производный класс (на любом уровне наследования), будут корректно удаляться.

Ответ 4

Я думаю, причина в том, что было бы действительно запутать удаление виртуальности частично через структуру наследования (у меня есть пример сложности ниже).

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

Но для полноты рассмотрите следующее на языке, на котором вы можете де виртуализировать:

class A
{
public:
    virtual void Foo() { }
};

class B : public A
{
public:
    void Foo() { } // De-virtualize
};

class C: public B
{
public:
    void Foo() { } // not virtual
};

void F1(B* obj)
{
    obj->Foo();
    static_cast<A*>(obj)->Foo();
}

C test_obj;
F1(test_obj);   // Which two methods are called here?

Вы можете создавать правила точно, какие методы будут называться, но очевидный выбор будет варьироваться от человека к человеку. Намного проще просто распространять виртуальность метода.