|
Программирование >> Поддержка объектно-ориентированного программирования
class window w menu : public virtual window { класс окно с меню определения, связанные с меню void draw(); Теперь хотелось бы определить окно с рамкой и меню: class window w border and menu : public virtual window, public window w border, public window w menu { класс окно с рамкой и меню void draw(); Каждый производный класс добавляет новые свойства окна. Чтобы воспользоваться комбинацией всех этих свойств, мы должны гарантировать, что один и тот же объект класса window используется для представления вхождений базового класса window в эти производные классы. Именно это обеспечивает описание window во всех производных классах как виртуального базового класса. Можно следующим образом изобразить состав объекта класса window w border and menu: Чтобы увидеть разницу между обычным и виртуальным наследованием, сравните этот рисунок с рисунком из $$6.5, показывающим состав объекта класса satellite. В графе наследования каждый базовый класс с данным именем, который был указан как виртуальный, будет представлен единственным объектом этого класса. Напротив, каждый базовый класс, который при описании наследования не был указан как виртуальный, будет представлен своим собственным объектом. Теперь надо написать все эти функции draw(). Это не слишком трудно, но для неосторожного программиста здесь есть ловушка. Сначала пойдем самым простым путем, который как раз к ней и ведет: void window w border::draw() window::draw(); рисуем рамку void window w menu::draw() window::draw(); рисуем меню Пока все хорошо. Все это очевидно, и мы следуем образцу определения таких функций при условии единственного наследования ($$6.2.1 ), который работал прекрасно. Однако, в производном классе следующего уровня появляется ловушка: void window w border and menu::draw() ловушка! window w border::draw(); window w menu::draw(); теперь операции, относящиеся только к окну с рамкой и меню На первый взгляд все вполне нормально. Как обычно, сначала выполняются все операции, необходимые для базовых классов, а затем те, которые относятся собственно к производным классам. Но в результате функция window::draw() будет вызываться дважды! Для большинства графических программ это не просто излишний вызов, а порча картинки на экране. Обычно вторая выдача на экран затирает первую. Чтобы избежать ловушки, надо действовать не так поспешно. Мы отделим действия, выполняемые базовым классом, от действий, выполняемых из базового класса. Для этого в каждом классе введем функцию draw(), которая выполняет нужные только для него действия, а функция draw() будет выполнять те же действия плюс действия, нужные для каждого базового класса. Для класса window изменения сводятся к введению излишней функции: class window { головная информация void draw(); void draw(); Для производных классов эффект тот же: class window w border : public virtual window { класс окно с рамкой определения, связанные с рамкой void draw(); void draw(); void window w border::draw() window:: draw(); draw(); рисует рамку Только для производного класса следующего уровня проявляется отличие функции, которое и позволяет обойти ловушку с повторным вызовом window::draw(), поскольку теперь вызывается window:: draw() и только один раз: class window w border and menu : public virtual window, public window w border, public window w menu { void draw(); void draw(); void window w border and menu::draw() window:: draw(); window w border:: draw(); window w menu:: draw(); draw(); теперь операции, относящиеся только к окну с рамкой и меню Не обязательно иметь обе функции window::draw() и window:: draw(), но наличие их позволяет избежать различных простых описок. В этом примере класс window служит хранилищем общей для window w border и window w menu информации и определяет интерфейс для общения этих двух классов. Если используется единственное наследование, то общность информации в дереве классов достигается тем, что эта информация передвигается к корню дерева до тех пор, пока она не станет доступна всем заинтересованным в ней узловым классам. В результате легко возникает неприятный эффект: корень дерева или близкие к нему классы используются как пространство глобальных имен для всех классов дерева, а иерархия классов вырождается в множество несвязанных объектов. Существенно, чтобы в каждом из классов-братьев переопределялись функции, определенные в общем виртуальном базовом классе. Таким образом каждый из братьев может получить свой вариант операций, отличный от других. Пусть в классе window есть общая функция ввода get input(): class window { головная информация virtual void draw(); virtual void get input(); В одном из производных классов можно использовать эту функцию, не задумываясь о том, где она определена: class window w banner : public virtual window { класс окно с заголовком void draw(); void update banner text(); void window w banner::update banner text() ... get input(); изменить текст заголовка В другом производном классе функцию get input() можно определять, не задумываясь о том, кто ее будет использовать: class window w menu : public virtual window { класс окно с меню определения, связанные с меню void draw(); void get input(); переопределяет window::get input() Все эти определения собираются вместе в производном классе следующего уровня: class window w banner and menu : public virtual window, public window w banner, public window w menu void draw(); Контроль неоднозначности позволяет убедиться, что в классах-братьях определены разные функции: class window w input : public virtual window { ... void draw(); void get input(); переопределяет window::get input class window w input and menu : public virtual window, public window w input, public window w menu { ошибка: оба класса window w input и window w menu переопределяют функцию window::get input void draw(); Транслятор обнаруживает подобную ошибку, а устранить неоднозначность можно обычным способом: ввести в классы window w input и window w menu функцию, переопределяющую функцию-нарушителя , и каким-то образом устранить неоднозначность: class window w input and menu
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |