Единственными функциями-членами, к которым может применяться спецификатор final
, являются виртуальные функции-члены. Вполне вероятно, что в AssertionException
или в одном из его собственных базовых классов этот член определен как
virtual void defineOnlyInFinalSubclassToPreventSlicing() = 0;
Таким образом, все классы в иерархии, кроме самых производных, являются абстрактными базовыми классами. Нельзя создавать значения абстрактных классов (они могут служить только основами). И поэтому нельзя случайно написать
try {
foo();
}
catch(AssertionException const e) { // oops catching by value
}
Если бы AssertionException
не было абстрактным, вышеприведенное можно было бы написать. Но когда он будет абстрактным, компилятор будет жаловаться на этот обработчик исключений, заставляя нас ловить ссылки. И отлов по ссылке - рекомендуемая практика.
Маркировка члена (и класса) как final
гарантирует, что дальнейшее вывод невозможно. Таким образом, проблема не может появиться случайно при изменении иерархии наследования. Потому что программист, который добавляет другой класс и снова определяет defineOnlyInFinalSubclassToPreventSlicing
как final, вызовет ошибку у компилятора, поскольку этот элемент уже объявлен как final в базе. Поэтому им придется удалить реализацию из базового класса, тем самым снова делая ее абстрактной.
Это бухгалтерская система.