Помимо того, что можно дополнить/изменить/скрыть функциональность существует возможность и останить иерархию как наследования, так и переопределения. Для этого существуют финальные методы/классы.
struct A {
virtual void foo() { cout << "A"; }
// void bar() final; // недопустимо
};
struct B : A { // И делаем финальным классом, без возможности
void foo() final { cout << "B"; }
};
struct С final : A { // C делаем финальным классом, без возможности отнаследоваться от него
};
struct D : B {
//void foo() { cout << "D"; } // недопустимо, так как в B метод foo финальный
};
/*
struct E : C { }; // недопустимо, так как C - финальный класс
*/