Ответ 1
С++ 11 для этой цели добавлено контекстное ключевое слово final
.
class VectIterator : public Iterator<T>
{
public:
T& next() final { ... }
...
};
struct Nope : VecIterator {
T& next() { ... } // ill-formed
};
В чем причина удаления возможности остановить распространение методов виртуальности?
Позвольте мне быть яснее: на С++, напишите ли вы "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 существует.
С++ 11 для этой цели добавлено контекстное ключевое слово final
.
class VectIterator : public Iterator<T>
{
public:
T& next() final { ... }
...
};
struct Nope : VecIterator {
T& next() { ... } // ill-formed
};
Простой ответ: Не смешивайте конкретные и абстрактные интерфейсы! Очевидным подходом в вашем примере будет использование функции virtual
virtual
, которая делегирует функции virtual
, например, do_next()
. Производный класс переопределит do_next()
, возможно, делегируя функции virtual
virtual
. Поскольку функции next()
вероятны inline
, в деле делегирования не требуется никаких затрат.
По моему мнению, одной из веских причин этого распространения является деструкторы virtual
. В С++, когда у вас есть базовый класс с некоторыми методами virtual
, вы должны определить деструктор virtual
. Это связано с тем, что у некоторого кода может быть указатель базового класса, который фактически указывает на производный класс, а затем пытается удалить этот указатель (подробнее см. question).
Определив деструктор в базовом классе как vritual
, вы можете убедиться, что все указатели базового класса, указывающие на производный класс (на любом уровне наследования), будут корректно удаляться.
Я думаю, причина в том, что было бы действительно запутать удаление виртуальности частично через структуру наследования (у меня есть пример сложности ниже).
Однако, если вы обеспокоены микро-оптимизацией удаления нескольких виртуальных вызовов, я бы не стал беспокоиться. Пока вы встраиваете код метода виртуального ребенка, И ваш итератор передается по значению, а не ссылке, хороший оптимизирующий компилятор уже сможет видеть динамический тип во время компиляции и встроить все это для вас, несмотря на это. являясь виртуальным методом!
Но для полноты рассмотрите следующее на языке, на котором вы можете де виртуализировать:
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?
Вы можете создавать правила точно, какие методы будут называться, но очевидный выбор будет варьироваться от человека к человеку. Намного проще просто распространять виртуальность метода.