Важнейшую проблему "правильного" наследования я начну с термина
замещаемость
(substitutability), взятого у Маршалла Клайна (Marshall Cline) и Грега Ломау (Greg Lomow) (C++ FAQs, Addison-Wesley, 1998). Этот термин означает, что поведение производного класса достигается путем замещения поведения, заимствованного у базового класса. Это одно из важнейших правил, которое вам нужно соблюдать при построении
работающей
иерархии классов. (Под "работающими" я подразумеваю системы, выдержавшие проверку временем и оправдавшие надежды на повторное использование и расширение кода.)
А вот еще одно важное правило, которому я советую следовать при создании собственной иерархии классов:
любой унаследованный интерфейс производного класса не должен требовать больше и обещать меньше, чем в базовом классе.
Пренебрежение этим правилом приводит к разрушению существующего кода. Интерфейс класса — это контракт между классом и пользователями, применяющими этот класс. Имея ссылку на производный класс, программист всегда может обращаться с ним, как с базовым классом. Это называется
восходящим преобразованием типа
(upcasting). В нашем примере клиент, имея ссылку на объект
ContractEmp-loyee,