7.4. Множественное наследование в C++
Так же, как язык CLOS представляет собой объектно-ориентированное расширение языка LISP, так и язык C++ создан на основе широко известного языка С и сохранил все его возможности, добавив к ним средства объектно-ориентированного программирования. Если отвлечься от того факта, что CLOS и C++ основаны на разных языках-прототипах, то основное отличие между ними заключается в реализации механизма наследования, в частности множественного наследования. В языке C++ множественное наследование трактуется совсем не так, как мы это делали в предшествующих разделах настоящей главы, а потому этот вопрос заслуживает подробного обсуждения.
В языке C++ родовые операции реализуются в виде виртуальных функций. Виртуальная функция, объявленная в классе X, это функция, которая может быть перегружена (переопределена) в классе, производном от X. При объявлении в классе X виртуальная функция вообще может не иметь тела, т.е. программного кода реализации. В таком случае функция называется чисто виртуальной, а класс, имеющий одну или более чисто виртуальных функций, является абстрактным базовым классом, экземпляры которого создать невозможно. В любом случае ключевое слово virtual говорит компилятору, что программный код функции будет уточнен в производных классах.
Те методы, которые вызываются на выполнение, являются невиртуальными членами-функциями, т.е. функциями, имеющими определенный программный код, который не перегружается в производных классах. В этом смысле C++ существенно отличается от языка CLOS, в котором практически все функции суперкласса в большей или меньшей степени модифицируются механизмом наложения методов. Поэтому в C++ существует множество синтаксических тонкостей, в которых не нуждается CLOS. Например, во всех классах иерархии виртуальная функция должна иметь квалификатор virtual до тех пор, пока в некотором производном классе не будет представлена ее конкретная реализация.
В чисто иерархической структуре классов, когда каждый производный класс имеет единственного "родителя", передача методов по наследству выполняется совершенно очевидным способом. Единственная тонкость в реализации этого механизма в C++ состоит в использовании квалификаторов наследования public и private. Если не вдаваться в подробности, то наследование вида public представляет собой отношение "is а" (является), которое мы использовали при обсуждении фреймов. Наследование вида private ближе к отношению "реализовано в терминах", которое позволяет скрыть определенные детали реализации интерфейсов объектов. Такое полезное разделение "выпало" в языке CLOS, в котором каждое отношение "класс-подкласс" несет семантический смысл.
Но если обратиться к множественному наследованию, то механизмы его реализации в C++ и CLOS существенно отличаются. Поскольку в языке C++ не существует такого понятия, как порядок предшествования классов, то даже такой простой случай, как в рассмотренном выше примере "Алмаз Никсона", приводит к неоднозначности. Будем считать, что отношения между классами Person, Quaker, Republican и Republican_Quaker, как и прежде, имеют вид, представленный на рис. 7.8.
Объявление классов Person, Quaker, Republican и Republican Quaker на языке C++ показано в листинге 7.2 (программный код объявления включен в файл nixon.h).
Иллюстрированный самоучитель по Adobe_Illustrator_10 тут