|
Программирование >> Дополнительные возможности наследования
в теле профаммы используется указатель класса Animal, с помощью которого делаются ссылки на все объекты производных классов. В зависимости от того, с каким объектом связан указатель в текущий момент, вызываются соответствующие виртуальные функции. При попытке создать объекты для классов абстрактных типов данных Animal или Mammal компилятор покажет сообщение об ошибке. Когда сАвдувш использовать аОстрактиыв типы уаииых в одних примерах профамм, рассмофенных нами ранее, класс Animal являлся абстрактным типом данных, в других - нет. В каких же случаях нужно объявлять класс как абстрактный тип данных? Нет никаких правил, которые требовали бы объявления класса как абстрактного. Профаммист принимает решение о создании абсфактного типа данных, основываясь на том, какую роль ифает этот класс в профамме. Так, если вы хотите смоделировать виртуальную ферму или зоопарк, то имеет смысл класс Animal объявить как абстрактный и для создания объектов производить от него другие классы, такие как Dog. Если же вы хотите смоделировать виртуальную псарню, то теперь класс Dog будет аб-сфактным, от которого можно производить подклассы, представляющие разные породы собак. Количество уровней абсфактных классов следует выбирать в зависимости от того, насколько детально вы хотите смоделировать реальный объект или явление. Нв рвкомвндувтся Используйте абстракгнью типь( данных для создания общего интерфейса для всех производных классов. Обязательно замещайте в производных классах все чистью виртуальнью функции. Объявляйте все функции, которые нужно замещать в производных классах, как чистые виртуальные функции. Не пытайтесь создать объект абстрактного класса. Логика использования аОстрактных классов в последнее время в профаммировании на С++ активно используется концепция создания абстрактных логических конструкций. С помощью таких конструкций можно находить решения для многих общих задач и создавать при этом профаммы, которые легко читаются и документируются. Рассмотрим пример создания логической конструкции с использованием наследования классов. Представим, что нужно создать класс Timer, который умеет отсчитывать секунды. Такой класс может иметь целочисленную переменную-член itsSeconds, а также метод, осуществляющий приращение переменной itsSeconds. Теперь предположим, что профамма должна отслеживать и сообщать о каждом изменении переменной itsSeconds. Первое решение, которое приходит на ум, - это добавить в класс Timer метод уведомления об изменении переменной-члена. Но логически это не совсем верно, так как профамма уведомления может быть достаточно сложной и по сути своей не является логической частью профаммы отсчета времени. Гораздо логичнее рассматривать профамму отслеживания и информирования об изменении переменной как абстрактный класс, который в равной степени может использоваться как с профаммой отсчета времени, так и с любой другой профаммой с периодически изменяющимися переменными. Таким образом, лучщим рещением будет создание абстрактного класса обозревателя Observer с чистой виртуальной функцией Update(). Теперь создадим второй абстрактный класс - Subject. Он содержит массив объектов класса Observer. Кроме того, в нем объявлены два дополнительных метода: RegisterO, который регистрирует объекты класса Observer, и NotifyO, который отслеживает изменения указанной переменной. Эта конструкция классов может затем использоваться во многих профаммах. Те классы, которые будут отслеживать изменения и сообщать о них, наследуются от класса Observer. Класс Timer в нащем примере наследуется от класса Subject. При изменении контролируемой переменной (в нащем примере - itsSeconds) вызывается метод NotifyO, унаследованный от класса Subject. Наконец, можно создать новый класс ObserverTimer, унаследованный сразу от двух базовых классов - Observer и Timer, который будет сочетать в себе возможности отсчитывать время и сообщать об этом. Пара слов о множвстввнном наслвдованцц, аОстрактных типах данных н языке Java Многие профаммисты знают, что в основу языка Java положен С++. Также известно, что создатели языка Java удалили из него возможность множественного наследования потому, что, по их мнению, это средство слищком усложняет профаммный код и идет в разрез с концепцией упрощения профаммных кодов, положенной в основу Java. С точки зрения создателей Java, 90% всех возможностей, предоставляемых множественным наследованием, можно получить с помощью интерфейса. Интерфейс в терминологии Java представляет собой нечто подобное абстрактному типу данных, в том смысле, что в нем также определяются функции, которые могут быть реализованы только в производных классах. Но новые классы не производятся непосредственно от интерфейса. Классы производят от других классов и в них передаются функции интерфейса, что напоминает множественное наследование. Так, союз абстрактных классов и множественного наследования породил на свет аналог классов-мандатов, в результате чего удалось избежать чрезмерного усложнения профаммных кодов, как в случае с множественным наследованием. Кроме того, поскольку интерфейсы не содержат ни выполняемых функций, ни переменных-членов, отпадает необходимость в виртуальном наследовании. Насколько удобны или целесообразны эти изменения, зависит от привычек конкретного профаммиста. Во всяком случае, если вы хорощо разберетесь в множественном наследовании и абстрактных типах данных языка С++, то это послужит хорошей базой при изучении и освоении последних достижений и тенденций профаммирования, реализованных в языке Java (если у вас возникнет интерес к нему). Использование логических конструкций в языках С++ и Java подробно рассматривается в следующей статье: Robert Martin, С++ and Java: A Critical Comparison С++ Report. - January, 1997. Резюме Сегодня вы познакомились с методами преодоления некоторых ограничений одиночного наследования.-Вы узнали об опасности передачи вверх по иерархии классов интерфейса производных функций и об ограничениях приведения типа данных объектов базового класса к производным классам во время выполнения программы. Кроме того, вы узнали, когда и как используется множественное наследование классов, какие проблемы при этом могут возникнуть и как их преодолеть. На этом занятии также было представлено объявление абстрактных типов данных и способы создания абстрактного класса с помощью чистых виртуальных функций. Особое внимание уделялось логике использования абстрактных данных для моделирования реальных ситуаций. Вопросы U ответы Что означает передача функциональности вверх по иерархии классов? Речь идет о переносе описаний общих функций-членов в базовые классы более высокого уровня. Если одна и та же функция используется в производных классах, имеет смысл описать эту функцию в общем для них базовом классе. Во всех ли случаях передача функциональности вверх целесообразна в программе? Если передаются вверх по иерархии только функции общего использования, то это целесообразно, но смысл теряется, если в базовые классы передается специфичный интерфейс производных классов. Другими словами, если метод не может быть использован во всех производных классах, то нет смысла описывать его в базовом классе. В противном случае вам во время выполнения профаммы придется отслеживать тип текущего объекта, прежде чем вызвать функцию. В чем проблема с контролем типа объекта при выполнении программы? В больших профаммах для выполнения контроля за типом объекта придется использовать достаточно массивный и сложный программный блок. Идея использования виртуальных функций состоит в том, что тип объекта определяется профаммой автоматически с помощью виртуальной таблицы, вместо того чтобы использовать для этого специальные профаммные блоки. Что плохого в приведении типа объектов? Приведение типов объектов к определенному типу данных, используемому конкретной функцией, довольно часто и эффективно используется в профаммах на С++. Но если программист применяет приведение типов для того, чтобы обойти заложенный в С++ строгий контроль за соответствием типов данньгх, например в случае приведения типа указателя к установленному во время выполнения профаммы типу объекта, то это говорит о серьезных недостатках в сфуктуре профаммы, противоречащих идеологаи С++. Почему бы не сделать все функции виртуальными? Для поддержания работы виртуальных функций создается виртуальная таблица, что увеличивает потребление памяти профаммой и время выполнения профаммы. Если в профамме используется небольшой класс, от которого не производятся подклассы, то в использовании виртуальных функций нет никакого смысла.
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |