Единичное наследование
:
{}
При уровне доступа public сохраняется уровень видимости элементов базового класса.
При уровне доступа private все элементы производного класса считаются приватными, то есть невидимыми для клиентов.
Кроме того, приватные производные классы закрывают доступ ко всем элементам классов-предков со стороны всех классов-потомков.Положим, что существует родительский класс:
Будем считать, что у этого класса есть публичный и приватный наследники: class Child_1: public Parent_class {...};
class Child_2: private Parent_class {...};
При приватном наследовании в экземплярах класса-наследника нет элементов, которые видят клиенты. Для восстановления видимости элемента применяется его повторное экспортирование. Оно отменяет скрытность элемента. Например:
class Child_3: private Parent_class {
Parent_class :: c;
} |
ООП на языке C++ |
479 |
Теперь в экземплярах С1"а^_3 доступен элемент с (как если бы наследование было публичным). Двойное двоеточие — это операция разрешения области видимости. Она задает класс, где определен элемент. Рассмотрим класс: |
Вложенный в него класс Element определяет элемент связанного списка, включающий два компонента — поле целого типа и указатель на элемент. Этот класс размещен в приватной секции, что скрывает его от других классов. Элементы- данные в Element публичны, то есть видимы в объемлющем классе Linked_list. Если бы они были приватными, то для обеспечения видимости элементов-данных пришлось бы объявить класс Element другом класса Linked_list. Заметим, что у вложенных классов нет никаких специальных прав доступа к элементам объемлющего класса. Только статические элементы-данные объемлющего класса видимы методам вложенного класса. Объемлющий класс Linked_list имеет один элемент-данные, это указатель на заголовок списка. Он содержит конструктор, обнуляющий значение указателя. Четыре элемента-функции обеспечивают вставку элементов в список (с головы или хвоста), удаление элементов (с головы), проверку пустоты списка. Определим классы стек и очередь, которые станут публичными потомками-на- следниками класса связанный список: |
продолжение # |
480 Глава 16. Объектно-ориентированное и аспектно-ориентированное программирование |
return Linked_list :: delete_at_head ( ); }; }; Заметим, что объекты как подкласса Stack, так и подкласса Queue имеют доступ к функции empty ( ) из родительского класса Linked_list (так как это публичное наследование). Новые методы подклассов push(), pop(), enqueue(), dequeue() на самом деле лишь вызывают соответствующие методы родительского класса, которые и выполняют необходимую работу. Оба подкласса содержат по конструктору (который ничего не делает). При создании объекта неявно вызывается конструктор подкласса. Далее вызывается конструктор родительского класса (в нашем примере именно он и выполняет необходимую инициализацию). Однако здесь возникает серьезная проблема. Поскольку наследование публичное, объект класса Stack имеет доступ к операции insert_at_tail ( ) родительского класса, а это разрушает целостность стека. Аналогично объекту класса Queue доступна операция insert_at_head ( ). Эти нежелательные вызовы вполне возможны, поскольку и класс Stack, и класс Queue являются подтипами типа Linked_list, а значит их экземпляры (в соответствии с принципом подстановки) могут использоваться вместо экземпляров Linked_list.Если задуматься, то следует сказать правду: функциональное сходство стека и очереди со связанным списком весьма сомнительного свойства и не выдерживает теста «является», поскольку эти дети слабо похожи на своего родителя. Следовательно, нужно отказаться от родства и применить «запретное» наследование для конструирования, которое в языке С++ поддерживается механизмом приватного наследования. Заметим, что приватным подклассам придется скопировать метод empty (), поскольку он будет скрыт от их экземпляров. В итоге получим следующие подклассы:
Обратим внимание, что потребовался повторный экспорт операции empty ( ), поскольку она стала скрытой. |
ООП на языке C++ |
481 |
Две версии стека и очереди иллюстрируют различия между подтипами и подклассами, которые не являются подтипами. Связанный список может считаться обобщением стеков и очередей, но только на уровне реализации. С этой точки зрения вполне естественно наследование от класса связанного списка для определения классов стек и очередь. Тем не менее ни один из них не называется подтипом связанного списка, поскольку превращает публичные элементы родительского класса в приватные, запрещая клиентам соответствующий доступ. |