C ++ последний класс и идиома нарезки - PullRequest
3 голосов
/ 30 января 2020

Мне довелось просматривать исходный код mongoDB, и я обнаружил эту интересную конструкцию:

class NonspecificAssertionException final : public AssertionException {
public:
    using AssertionException::AssertionException;

private:
    void defineOnlyInFinalSubclassToPreventSlicing() final {}
};

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

Приветствия, Джордж

1 Ответ

4 голосов
/ 30 января 2020

Единственными функциями-членами, к которым может применяться спецификатор final, являются виртуальные функции-члены. Вполне вероятно, что в AssertionException или в одном из его собственных базовых классов этот член определен как

virtual void defineOnlyInFinalSubclassToPreventSlicing() = 0;

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

try {
    foo();
}
catch(AssertionException const e) { // oops catching by value
} 

Если бы AssertionException не было абстрактным, вышеприведенное можно было бы написать. Но когда он будет абстрактным, компилятор будет жаловаться на этот обработчик исключений, заставляя нас ловить ссылки. И отлов по ссылке - рекомендуемая практика.

Маркировка члена (и класса) как final гарантирует, что дальнейшее вывод невозможно. Таким образом, проблема не может появиться случайно при изменении иерархии наследования. Потому что программист, который добавляет другой класс и снова определяет defineOnlyInFinalSubclassToPreventSlicing как final, вызовет ошибку у компилятора, поскольку этот элемент уже объявлен как final в базе. Поэтому им придется удалить реализацию из базового класса, тем самым снова делая ее абстрактной.

Это бухгалтерская система.

...