Реферат по предмету "Программирование"


Интерфейсы как решение проблем множественного наследования

Интерфейсы как решение проблем множественного
наследования

Евгений Каратаев

В
этой работе разбирается проблема множественного наследования в языке
программирования С++ и возможное ее решение путем применения абстракций
интерфейсов.

Множественным
наследованием является образование класса путем наследования одновременно
нескольких базовых классов. Штука полезная и одновременно с этим проблемная.
Разберем пример, в котором появляется множественное наследование, приводящее к
проблеме.

Классическим
заданием для начинающего программиста является задача написать классы,
реализующие иерархию Человек - Студент - Сотрудник. Обычно первым же решением
есть образование трех классов в виде:

class
Человек { ... };

class
Сотрудник : public Человек { ... };

class
Студент : public Человек { ... };

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

Проблема
возникает позже, когда оператор приходит и говорит:

-
У меня есть человек, который одновременно и сотрудник и студент. Что мне
делать?

Реализованная
схема, вообще говоря, не предполагает такого варианта - могут быть либо
сотрудник, либо студент. Но что-то делать надо. В этот момент приходит на
помощь множественное наследование. Программист, не долго думая, создает еще
один класс, образованный наследованием и от Сотрудник и от Студент:

class
СтудентСотрудник : public Студент, public Сотрудник { ...};

На
первый взгляд все в порядке, на второй - полный бардак. Дело в том, что
класс Сотрудник, как он был декларирован, содержит в себе полную копию класса
Человек. То же самое относится и к классу Студент. Таким образом, класс
СтудентСотрудник будет содержать в себе уже 2 копии класса Человек. При этом
функции класса Сотрудник будут работать со своим экземпляром класса Человек, а
функции класса Студент - со своим. В результате корректного поведения добиться
практически очень трудно. В классе СтудентСотрудник придется переопределять все
функции базовых классов и вызывать соответствующие функции базовых классов,
чтобы модификации обеих копий класса Человек прошли когерентно.

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

class
Человек { ... };

class
Студент : virtual public Человек { ... };

class
Сотрудник : virtual public Человек { ... };

class
СтудентСотрудник : public Студент, public Сотрудник { ...

};

В
этом варианте решена проблема однозначной входимости класса Человек во все
классы. Но остается вопрос - не возникнет ли такой же проблемы и дальше с
полученным классом СтудентСотрудник? И будет ли возможность произвести
модификацию уже работающего кода? В такой ситуации руки могут опуститься -
следует либо согласиться с существованием проблемного кода либо действительно
идти на полную переработку программы.

Тем
не менее элегантное решение существует. Это реализация базовых классов по
принципу интерфейсов. Язык С++ не содержит языковой поддержки интерфейсов в
явном виде, поэтому будем их эмулировать. Принцип интерфейса состоит в том, что
его задачей является не столько реализация класса, сколько его декларация.
Нормализуем исходную задачу:

class
БытьЧеловеком { ... };

class
БытьСтудентом { ... };

class
БытьСотрудником { ... };

Исходя
из нормализованного множества классов, получим дополнение:

class
Человек : public БытьЧеловеком { ... };

class
Сотрудник : public БытьЧеловеком, public БытьСотрудником { ... };

class
Студент : public БытьЧеловеком, public БытьСтудентом { ...};

class
СтудентСотрудник : public БытьЧеловеком, public БытьСтудентом,

   public БытьСотрудником { ... };

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

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

class
БытьСтудентом

{

   БытьЧеловеком& m_БытьЧеловеком;

   public:

   БытьСтудентом( БытьЧеловеком& init)

   : m_БытьЧеловеком( init)

   { ... };

};

class
Студент : public БытьЧеловеком, public БытьСтудентом

{

   public:

   Студент()

   : БытьЧеловеком(), БытьСтудентом( *this)

   { ...};

};

В
этой схеме, согласно стандарту, также есть проблема - стандарт не гарантирует
инициализации конструкторов, указанных в списке инициализации, в том порядке, в
котором они перечислены в этом списке. Поэтому мы, передавая *this как аргумент
конструктора базового класса, получаем ссылку на негарантированно определенный
объект. Выйти из этой ситуации можно, если декларировать конструктор без
аргументов и создать дополнительную функцию инициализации, зависящую от *this.
Но дублирование ссылок, хранимых в интерфейсных классах, тем не менее,
сохраняется и это есть некрасиво.

Для
решения этой задачи есть чрезвычайно красивое, на мой взгляд, решение. Решение
заключается в том, чтобы не хранить ссылку на ядро объекта, а получать ее
динамически. Для этого применяется оператор приведения типа dynamic_cast,
применяемый не к классу, а к объекту в процессе работы программы. Пример:

class
БытьСтудентом

{

   public:

      БытьСтудентом(){};

      virtual void Func( void);

      // пример функции, обращающейся к ядру
объекта

      {

         БытьЧеловеком* ptr = dynamic_cast( this);

         if( ptr)

         {

            // используем ядро

         }

      };

};

На
первый взгляд, приведение типа БытьСтудентом к типу БытьЧеловеком невозможно,
поскольку никто их этих классов ни от кого не наследован. Но дело в том, что
оператор dynamic_cast определен не для классов, а для объектов. И если при
исполнении кода Func реальный объект, для которого эта функция выполняется,
имееет класс, унаследованый от БытьЧеловеком, то оператор вернет правильное
значение. Согласно стандарту, оператор приведения типа dynamic_cast имеет два
вида поведения если приведение невозможно - вернуть нулевое значение либо
возбудить исключительную ситуацию. Оба варианта нас полностью устраивают.

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

Для
подготовки данной работы были использованы материалы с сайта http://karataev.nm.ru/


Не сдавайте скачаную работу преподавателю!
Данный реферат Вы можете использовать для подготовки курсовых проектов.

Поделись с друзьями, за репост + 100 мильонов к студенческой карме :

Пишем реферат самостоятельно:
! Как писать рефераты
Практические рекомендации по написанию студенческих рефератов.
! План реферата Краткий список разделов, отражающий структура и порядок работы над будующим рефератом.
! Введение реферата Вводная часть работы, в которой отражается цель и обозначается список задач.
! Заключение реферата В заключении подводятся итоги, описывается была ли достигнута поставленная цель, каковы результаты.
! Оформление рефератов Методические рекомендации по грамотному оформлению работы по ГОСТ.

Читайте также:
Виды рефератов Какими бывают рефераты по своему назначению и структуре.

Сейчас смотрят :

Реферат Общественное производство как основа развития страны
Реферат Led Zepplin Essay Research Paper Led ZeppelinHollywood
Реферат Ономатопоэтические слова японского языка в функции экспрессивной характеристики человека и их системные связи
Реферат Аббревиатуры в современном русском языке (на материале СМИ)
Реферат Булгарин Фаддей Венедиктович
Реферат А. В. Андрейченко Курганский филиал института экономики Уро ран, г. Курган
Реферат Прогнозирование развития образования на примере Новосибирской области
Реферат Роль международных корпораций и финансовых групп в мировой экономике
Реферат Себестоимость, расчет по экономическим элементам и калькуляционным статьям
Реферат Рыночная система.
Реферат Организация и учет долгосрочного кредитования
Реферат Экономическое развитие Западно-Сибирского региона
Реферат Расчет базовой импортной цены товара
Реферат Логопедическая работа с больными перенесшими инсульт
Реферат Під єднання до Інтернету за допомогою Windows 95 Dial-up Networking