Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
шпоры по ООП.doc
Скачиваний:
31
Добавлен:
25.09.2019
Размер:
1.04 Mб
Скачать
  1. Иерархия классов

Также как и в таксономии (классификации видов) иерархия классов в информатике означает классификацию объектных типов, рассматривая объекты как реализацию классов (класс похож на заготовку, а объект — это то, что строится на основе этой заготовки) и связывая различные классы отношениями наподобие «наследует», «расширяет», «является его абстракцией», «определение интерфейса».Отношения, установленные в области объектно-ориентированного проектирования и стандартах интерфейса объектов, определяются наиболее распространенным использованием, создателями языков (Java, C++, Smalltalk,Visual Prolog) и комитетами по стандартизации, как например, Object Management Group.

Производный класс сам в свою очередь может быть базовым классом:

class employee { /* ... */ };

class manager : public employee { /* ... */ };

class director : public manager { /* ... */ };

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

class temporary { /* ... */ };

class secretary : public employee { /* ... */ };

class tsec

: public temporary, public secretary { /* ... */ };

class consultant

: public temporary, public manager { /* ... */ };

39.Простые манипуляторы для управления потоком

Манипуляторы являются функциями, которые можно включать в цепочку последовательных операций помещения и извлечения. Это удобный способ управления флагами потока. Однако, применение манипуляторов этим не ограничивается. Изменения, внесенные в поток манипулятором, сохраняются до следующего использования того же манипулятора, за исключением манипулятора setw.

Различают простые и параметризованные манипуляторы. Простые манипуляторы не требуют аргументов, для обращения к ним используется только имя манипулятора (имя соответствующей функции). К простым манипуляторам относятся:

ws - пропуск начальных пробелов при вводе (или эквивалентных им символов);

dec - вывод числа по основанию 10;

oct - вывод числа по основанию 8;

hex - вывод числа по основанию 16;

flush - принудительно записывает все выходные данные на соответствующее физическое устройство;

endl - помещает в выходной поток символ новой строки ('\n') и вызывает манипулятор flush;

ends - помещает в выходной поток нулевой символ ('\0'). Обычно используется для указания конца строки.

Пример:

int i;

cout << "Введите число:";

cin >> i;

if (!cin) cout << "Ошибочный ввод ..." << endl;

else

cout << "Отображение в различных форматах:" << endl

"Hex - " << hex << i << endl

"Oct - " << oct << i << endl

"Dec - " << dec << i << endl;

  1. Прядок вызова конструкторов в производных классах

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

class A { }; class B { }; class C : public A, public B { };

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

class A { public: int x0, xA; }; class B : public A { public: int xB; }; class C : public A { public: int x0, xC; }; class D : public B, public C { public: int x0, xD; };

В этом примере класс A дважды используется при объявлении класса D в качестве непрямого базового класса. Для наглядного представления структуры производного класса также используются направленные ациклические графы, схемы классов и объектов.

Как и раньше, самый нижний узел направленного ациклического графа, а также нижний уровень схем соответствует производному классу и фрагменту объекта, представляющего производный класс. Такой фрагмент объекта мы будем называть производным фрагментом-представителем данного класса. Верхние узлы графа и верхние уровни схем классов и объектов соответствуют базовым классам и фрагментам объектов, представляющих базовые и непосредственные базовые классы. Эти фрагменты объекта мы будем называть базовыми и непосредственными базовыми фрагментами-представителями класса. Вот как выглядит граф ранее приведённого в качестве примера производного класса D:

A A B C D

А вот как представляется структура производного класса в виде неполной схемы класса. Базовые классы располагаются на этой схеме в порядке, который соответствует списку базовых элементов в описании базы производного класса. Этот же порядок будет использован при изображении диаграмм объектов. И это несмотря на то обстоятельство, что порядок вызова конструкторов базовых классов определяется конкретной реализацией. За порядком вызова конструкторов базовых классов всегда можно наблюдать после определения их собственных версий.

A B A C D

А вот и схема объекта производного класса.

D MyD; MyD ::= A (int)x0;

(int)xA;

B (int)xB;

A (int)x0; (int)xA;

C (int)x0;

D (int)x0;

(int)xD;

Первое, что бросается в глаза - это множество одноимённых переменных, "разбросанных" по базовым фрагментам объекта. Да и самих базовых фрагментов здесь немало.

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

MyD.xA = 100; здесь предпринимается неудачная попытка изменения значения данного-члена базового фрагмента объекта MyD. Выражение доступа MyD.xA именует сразу две переменных xA. Разрешение неоднозначности сводится к построению такого выражения доступа, которое однозначно указывало бы функцию, объект, тип (об этом позже!) или перечислитель. Наша очередная задача сводится к описанию однозначных способов доступа к данным-членам класса, расположенным в разных базовых фрагментах объекта. И здесь мы впервые сталкиваемся с ограниченными возможностями операции доступа.

MyD.B::x0 = 100; Этот оператор обеспечивает изменение значения данного-члена базового фрагмента - представителя класса B. Здесь нет никаких проблем, поскольку непосредственный базовый класс B наследует данные-члены базового класса A. Поскольку в классе B отсутствуют данные-члены с именем x0, транслятор однозначно определяет принадлежность этого элемента. Итак, доступ к данному-члену базового класса A "со стороны" непосредственного базового класса B не представляет особых проблем.