I happened to be browsing the source for mongoDB, and found this interesting construct:
class NonspecificAssertionException final : public AssertionException {
public:
using AssertionException::AssertionException;
private:
void defineOnlyInFinalSubclassToPreventSlicing() final {}
};
How does the private method prevent slicing? I can't seem to think of the problem case.
Cheers, George
The only member functions to which the final
specifier may be applied are virtual member functions. It is likely that in AssertionException
or one of it's own base classes, this member is defined as
virtual void defineOnlyInFinalSubclassToPreventSlicing() = 0;
Thus, all classes in the hierarchy save the most derived ones are abstract base classes. One may not create values of abstract classes (they can only serve as bases). And so, one may not accidentally write
try {
foo();
}
catch(AssertionException const e) { // oops catching by value
}
If AssertionException
was not abstract, the above could be written. But when it's abstract the compiler will complain at that exception handler, forcing us to catch by reference. And catching by reference is recommended practice.
Marking the member (and class) as final
ensures no further derivation is possible. So the problem cannot reappear accidentally when the inheritance hierarchy is changed. Because a programmer that adds another class and again defines defineOnlyInFinalSubclassToPreventSlicing
as final will elicit an error from the compiler, on account of this member already being declared final in the base. They will therefore have to remove the implementation from the base class, thus making it abstract again.
It's a bookkeeping system.