Программирование >>  Разработка устойчивых систем 

1 ... 145 146 147 [ 148 ] 149 150 151 ... 196


Джерри Шварц (Jerry Schwarz), автор библиотеки потоков ввода-вывода, неоднократно говорил, что если бы ему пришлось проектировать библиотеку заново, то он бы убрал множественное наследование из ее архитектуры и заменил его различными потоковыми буферами и операторами преобразования.

Класс А в этом случае является (непосредственно) базовым по отношению к В, поэтому имя В::f доминирует над A::f.

Отказ от множественного наследования

Принимая решение о применении множественного наследования, следует задать себе по крайней мере два вопроса.

Должен ли новый тип предоставлять открытые интерфейсы обоих классов? (Подумайте, нельзя ли вложить один класс в другой, чтобы в новом классе проявлялась лишь часть его интерфейса.)

Потребуется ли выполнять повышаюшее преобразование к обоим базовым классам (или к большему количеству базовых классов)?

Если хотя бы на один вопрос вы ответите нет , то без множественного наследования можно - и, скорее всего, нужно - обойтись.

Обращайте особое внимание на ситуации, в которых один класс должен проходить повышаюшее преобразование только как аргумент функции. В этом слзд1ае класс можно оформить как вложенный и включить в новый класс функцию автоматического преобразования типа для получения ссылки на вложенный объект. Всюду, где объект нового класса передается в аргументе функции, рассчитывающей получить вложенный объект, используется функция преобразования типа. Тем не менее, преобразование типа не годится для обычного полиморфного выбора функции; для этого необходимо наследование. Правила хорошего стиля проектирования гласят, что композиции следует отдавать предпочтение перед наследованием.

Расширение интерфейса

Одно из лучших применений множественного наследования связано с использованием кода, находящегося вне вашего контроля. Предположим, вам досталась библиотека с заголовочным файлом и откомпилированными функциями, но без исходных текстов. Библиотека представляет собой иерархию классов с виртуальными функциями и содержит глобальные функции, получающие указатели на базовый класс библиотеки; иначе говоря, библиотечные объекты используются полиморфно. Теперь допустим, вы строите на базе этой библиотеки свое приложение и пишете собственный код с полиморфным применением базового класса.

Позднее, на стадии разработки или сопровождения проекта, вдруг выясняется, что интерфейс базового класса, предоставляемый разработчиком, вас не устраивает: какая-нибудь его невиртуальная функция должна быть виртуальной, или же в нем отсутствует виртуальная функция, необходимая для решения вашей задачи. Возможно, вам поможет множественное наследование.

Рассмотрим примерный заголовочный файл библиотеки:

: С09:Vendor.h

Заголовок класса, предоставленный разработчиком.



В вашам распоряжении имеется только он и откомпилированный файл Vendor.obj. fifndef VENDOR H fdefine VENDOR H

class Vendor { public: virtual void v() const:

void f() const: Может, функцию нужно сделать виртуальной... -VendorO: Деструктор не виртуален!

class Vendorl : public Vendor { public:

void v() const:

void f() const:

-VendorlO:

void A(const Vendors): void BCconst Vendors): И т. д.

fendif VENDOR H 111 -

Конечно, библиотека в действительности содержит больше производных классов и обладает более обширным интерфейсом. Обратите внимание на функции А() и В(), которые получают ссылку на базовый класс и интерпретируют ее полиморфно. Вот как выглядит файл реализации библиотеки:

: С09:Vendor.срр {0}

Этот код откомпилирован и недоступен

для пользователя библиотеки.

findude Vendor.h

finclude <iostreani>

using namespace std:

void Vendor::v() const { cout Vendor::v() endl: }

void Vendor::f() const { cout Vendor::f() endl: }

Vendor::-VendorО { cout -VendorO endl: }

void Vendorl::vO const { cout Vendorl::v() endl: }

void Vendorl::f() const { cout Vendorl::f() endl: }

Vendorl: :~Vendorl() { cout -VendorlO endl: }

void ACconst Vendors V) { ... V.vO: V.fO: ..

void B(const Vendors V) {

...

V.vO:

V.fO:

.. } /:-



Но в вашем проекте исходные тексты недоступны. Вместо этого вы получаете откомпилированный файл Vendor.obj или Vendor.lib (или с другим расширением, принятым в вашей системе).

При использовании библиотеки возникают проблемы. Во-первых, деструктор базового класса не является виртуальным. Во-вторых, функция f() тоже не является виртуальной; видимо, разработчик библиотеки решил, что это не обязательно. В-третьих, в интерфейсе базового класса отсутствует функция, абсолютно необходимая для решения вашей задачи. Также предположим, что вы уже написали довольно-таки объемистый код с использованием сушествуюшего интерфейса (не говоря уже о неподконтрольных вам функциях А() и В()), и изменять его не хочется.

Чтобы выйти из положения, создайте собственный интерфейс класса и новый набор производных классов от вашего интерфейса и сушествующих классов:

: C09:Paste.cpp {L} Vendor

Решение проблемы с использованием множественного наследования finclude <1ostreani> linclude Vendor.h using namespace std:

class MyBase { Исправление интерфейса Vendor public:

virtual void vO const = 0:

virtual void fO const = 0:

Новая интерфейсная функция:

virtual void g() const = 0:

virtual -MyBaseO { cout ~MyBase()\n : }

class Pastel : public MyBase. public Vendorl { public: void vO const {

cout Pastel::v() endl:

Vendorl::v():

void fO const { cout Pastel::f() endl: Vendorl::f():

void g() const { cout Pastel::g() endl:

-PastelО { cout -PastelO endl: }

int mainO { Pastels pip = *new Pastel: MyBaseS mp = pip: Повышающее преобразование cout calling f()\n : mp.fO: Правильное поведение cout calling g()\n : mp.gO: Новое поведение cout calling A(plp)\n :

Мы встречали такое в коммерческих библиотеках С++, по крайней мере, в ранних.



1 ... 145 146 147 [ 148 ] 149 150 151 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика