Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Множественное наследование




При множественном наследовании у класса может быть более одного предка. В этом случае класс наследует методы всех предков. Достоинства такого подхода в большей гибкости. Множественное наследование реализовано в C++. Из других языков, предоставляющих эту возможность, можно отметить Python и Eiffel. Множественное наследование поддерживается в языке UML.

Множественное наследование — потенциальный источник ошибок, которые могут возникнуть из-за наличия одинаковых имен методов в предках. В языках, которые позиционируются как наследники C++ (Java, C# и др.), от множественного наследования было решено отказаться в пользу интерфейсов. Практически всегда можно обойтись без использования данного механизма. Однако, если такая необходимость все-таки возникла, то, для разрешения конфликтов использования наследованных методов с одинаковыми именами, возможно, например, применить операцию расширения видимости — «::» — для вызова конкретного метода конкретного родителя.

Большинство современных объектно-ориентированных языков программирования (C#, Java, Delphi и др.) поддерживают возможность одновременно наследоваться от класса-предка и реализовать методы нескольких интерфейсов одним и тем же классом. Этот механизм позволяет во многом заменить множественное наследование — методы интерфейсов необходимо переопределять явно, что исключает ошибки при наследовании функциональности одинаковых методов различных классов-предков.

C++

Наследование в C++:

class A{ //базовый класс}; class B: public A{ //public наследование}; class C: protected A{ //protected наследование}; class Z: private A{ //private наследование};

В C++ существует три типа наследования: public, protected, private. Спецификаторы доступа членов базового класса меняются в потомках следующим образом:

Если класс объявлен как базовый для другого класса со спецификатором доступа public, тогда public члены базового класса доступны как public члены производного класса, protected члены базового класса доступны как protected члены производного класса.

Если класс объявлен как базовый для другого класса со спецификатором доступа protected, тогда public и protected члены базового класса доступны как protected члены производного класса.

Если класс объявлен как базовый для другого класса со спецификатором доступа private, тогда public и protected члены базового класса доступны как private члены производного класса.

Одним из основных преимуществ public-наследования является то, что указатель на классы-наследники может быть неявно преобразован в указатель на базовый класс, то есть для примера выше можно написать:

A* a = new B();

Агрегирование

В объектно-ориентированном программировании под агрегированием (также называемом композицией или включением) подразумевают методику создания нового класса из уже существующих классов путём включения, называемого также делегированием. Об агрегировании также часто говорят как об «отношении принадлежности» по принципу «у машины есть корпус, колёса и двигатель».

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

На базе агрегирования реализуется методика делегирования, когда поставленная перед внешним объектом задача перепоручается внутреннему объекту, специализирующемуся на решении задач такого рода.

Пример

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

Агрегация

Агрегация (агрегирование по ссылке) — отношение «часть-целое» между двумя равноправными объектами, когда один объект (контейнер) имеет ссылку на другой объект. Оба объекта могут существовать независимо: если контейнер будет уничтожен, то его содержимое — нет.

class Professor; class Department{ private: Professor* members[5];};

Композиция

Композиция (агрегирование по значению) — более строгий вариант агрегирования, когда включаемый объект может существовать только как часть контейнера. Если контейнер будет уничтожен, то и включённый объект тоже будет уничтожен.

class Department; class University{ private: Department faculty[20];};// Compositionclass Carburetor; class Automobile{ private: Carburetor* itsCarb; public: Automobile() {itsCarb=new Carburetor();} virtual ~Automobile() {delete itsCarb;}};

Метакласс

Метакласс — в объектно-ориентированном программировании это класс, экземпляры которого в свою очередь являются классами


 

4. Объект. Отношения между объектами. Реализация на C++, Common LISP.

Объект

Объекто-ориентированное программирование характеризуется наличием двух основных видов абстракций:

· Тип данных объектной природы (класс) – определяемое программистом расширение исходных типов языка

· Экземпляр класса (объект) – переменная класса. Объектов в сложной системе обычно гораздо больше, чем классов. Объекта обладает состоянием, поведением и идентичностью.

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

Предположим, что на языке C++ нам нужно создать регистрационные записи о сотрудниках.

ПРИМЕР

На практике принято ограничивать доступ к состоянию объекта, а не делать его общедоступным, как в предыдущем определении класса. С учетом сказанного, изменим данное определение следующим образом:

class PersonnelRecord {
public:

char* employeeName() const;
int employeeSocialSecurityNumber() const;
char* employeeDepartment() const;

protected:

char name[100];
int socialSecurityNumber;
char department[10];
float salary;

};

В частности, в новом определении реализация класса скрыта от других объектов. Если реализация класса будет в дальнейшем изменена, код придется перекомпилировать, но семантически клиенты не будут зависеть от этих изменении (то есть их код сохранится). Кроме того, решается также проблема занимаемой объектом памяти за счет явного определения операций, которые разрешены клиентам над объектами данного класса. В частности, мы даем всем клиентам право узнать имя, код социальной защиты и место работы сотрудника, но только особым клиентам (а именно, подклассам данного класса) разрешено устанавливать значения указанных параметров. Только этим специальным клиентам разрешен доступ к сведениям о заработной плате. Другое достоинство последнего определения связано с возможностью его повторного использования.

Состояние объекта – результат его поведения, т.е. выполнения в определённой последовательности характерных для него действий (при включении переключателя указателя поворотов лампочка указателя начинает мигать).

Поведение объекта определяется выполняемыми над ним операциями и его состоянием, причем некоторые операции имеют побочное действие: они изменяют состояние.

ПРИМЕР. Класс геометрическая фигура. Его методы могут менять положение фигуры на экране.

class figura

{ TPoint *points;

int N;

TColor Color1;

TPaintBox *myPicture;

TPoint convertcart (TPoint);

TPoint convertcanv (TPoint);

public:

figura(int,TPaintBox *);

~ figura();

void setpoint(TPoint,int)

void draw();

void erase();};

Идентичность – это такое свойство (или набор свойств) объекта, которое позволяет отличить его от всех прочих объектов этого же типа. Идентичность не обязательно связана с именованием или состоянием объекта: два совершенно одинаковых автомобиля, стоящих в одном и том же гараже, всё же являются разными объектами. Свойством, отвечающим за идентичность, в этом случае, может считаться VIN автомобиля (Vehicle Identification Number) – идентификационный номер автотранспортного средства).

Пример

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

class DisplayItem {

public:

DisplayItem();

DisplayItem(const Point& location);

~DisplayItem();

void draw();

void erase();

void select();

void unselect();

void move(const Point& location);

int isSelected() const;

Point location() const;

int isUnder(const Point& location) const;

protected:

... };

isUnder, должен вычислять, накрывает ли объект данную точку, а не просто возвращать значение какого-то свойства.

Объявим экземпляры указанных классов:

DisplayItem item1;

DisplayItem* item2 = new DisplayItem(Point(75, 75));

DisplayItem* item3 = new DisplayItem(Point(100, 100));

DisplayItem* item4 = 0;

При выполнении этих операторов возникают четыре имени и три разных объекта. Конкретно, в памяти будут отведены четыре места под имена item1, item2, item3, item4. При этом item1 будет именем объекта класса DisplayItem, а три других будут указателями. Кроме того, лишь item2 и item3 будут на самом деле указывать на объекты класса DisplayItem. У объектов, на которые указывают item2 и item3, к тому же нет имен, хотя на них можно ссылаться "разыменовывая" соответствующие указатели: например, *item2. Поэтому мы можем сказать, что item2 указывает на отдельный объект класса DisplayItem, на имя которого мы можем косвенно ссылаться через *item2. Уникальная идентичность (но не обязательно имя) каждого объекта сохраняется на все время его существования, даже если его внутреннее состояние изменилось. Эта ситуация напоминает парадокс Зенона о реке: может ли река быть той же самый, если в ней каждый день течет разная вода?

В качестве простейшего примера абстракции, интуитивно понятного всем, Эккель в своих книгах приводит электрическую лампочку, для которой определены, как минимум, два присущих ей состояния: включена (светит) и выключена (не светит).

Процесс влючения и выключения лампочки описывает поведение объекта. Включению лампы на уровне абстрактной модели соответствует функция TurnOn(), выключению – TurnOff(). Для того, чтобы узнать, светится лампа или нет, нам достаточно посмотреть на неё. На уровне асбтрактной модели этот процесс представлен специальной функцией IsTurnedOn(), позволяющей установить текущее состояние модельного объекта.

Время жизни объектов

Началом времени существования любого объекта является момент его создания (отведение участка памяти), а окончанием - возвращение отведенного участка памяти системе.

Объекты создаются явно или неявно. Есть два способа создать их явно. Во-первых, это можно сделать при объявлении (как это было с item1): тогда объект размещается в стеке. Во-вторых, как в случае item3, можно разместить объект, то есть выделить ему память из "кучи". В C++ в любом случае при этом вызывается конструктор, который выделяет известное ему количество правильно инициализированной памяти под объект.

Часто объекты создаются неявно. Так, передача параметра по значению в C++ создает в стеке временную копию объекта. Более того, создание объектов транзитивно: создание объекта тянет за собой создание других объектов, входящих в него. Переопределение семантики копирующего конструктора и оператора присваивания в C++ разрешает явное управление тем, когда части объекта создаются и уничтожаются. К тому же в C++ можно переопределять и оператор new, тем самым изменяя политику управления памятью в "куче" для отдельных классов.

В C++, объекты, созданные в стеке, уничтожаются при выходе из блока, в котором они были определены, но объекты, созданные в "куче" оператором new, продолжают существовать и занимать место в памяти: их необходимо явно уничтожать оператором delete. Если объект "забыть", не уничтожить, это вызовет утечку памяти. Если же объект попробуют уничтожить повторно (например, через другой указатель), последствием будет сообщение о нарушении памяти или полный крах системы.

При явном или неявном уничтожении объекта в C++ вызывается соответствующий деструктор. Его задача не только освободить память, но и решить, что делать с другими ресурсами, например, с открытыми файлами.

Отношение между объектами

В процессе взаимодействия объектов реализуется система.

Отношения двух любых объектов основываются на предположениях, которыми один владеет относительно другого: об операциях, что можно выполнять, и об ожидаемом поведении. Особенный интерес для объектно-ориентированного анализа и проектирования представляют два типа иерархических соотношений объектов: связь и агрегация. Эти два типа отношений можно назвать отношениями старшинства и "отец/потомок" соответственно.

Связь объектов

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

Принимая участие в связи, объект может выполнять одну из следующих трех ролей:

1. Актер. Объект может влиять на другие объекты, но сам никогда не поддается влиянию других объектов; в определенном содержании это отвечает понятию активный объект;

2. Сервер. Объект может лишь поддаваться влиянию со стороны других объектов, но он никогда не выступает в роли объекта, который влияет;

3. Агент. Такой объект может выступать как в активной, так и в пассивной, роли; как правило, объект-агент создается для выполнения операций в интересах объекта-актера или другого объекта-агента.

На рис. 3-2 показано несколько разных связей. Они отмечены линиями и означают как бы пути прохождения сообщений. Сами сообщения показаны стрелками (соответственно их направлению) и помечены именем операции. На рисунке объект aController связан с двумя объектами класса DisplayItem (объекты a и b). В свою очередь, оба, вероятно, связаны с aView, но нам была интересна только одна из этих связей. Только вдоль связи один объект может послать сообщение другому.

Связь между объектами и передача сообщений обычно односторонняя (как на рисунке; хотя технически она может быть и взаимной). Заметьте также, что хотя передаваемое сообщение инициализировано клиентом (в данном случае aController), данные передаются в обоих направлениях. Например, когда aController вызывает операцию move для пересылки данных объекту а, данные передаются от клиента серверу, но при выполнении операции isUnder над объектом b, результат передается от сервера клиенту.

Рис. 3-2. Связи.

На рис. 3-2 объект aController выступает как актер, объект a - как агент и объект aView - как сервер.

 

При обеспечении связей между объектами нужно позаботиться об их видимости и синхронизации.

Видимость. Пусть есть два объекта А и В и связь между ними. Для того, чтобы А мог послать сообщение В, нужно, чтобы В был в каком-то содержании видимым для А. Ми можем не заботиться об этом на стадии анализа, но когда дело доходит до реализации системы, мы должны обеспечить видимость связанных объектов.

В принципе есть следующие четыре способа обеспечить видимость.

1. Сервер является глобальным относительно клиента;

2. Сервер (или указание на него) передан клиенту как параметр операции;

3. Сервер является частью клиента.

4. Сервер локально порождается клиентом в ходе выполнения какой-либо операции.

Синхронизация. Когда один объект посылает за связью сообщение другому, связанному с ним, они, как говорят, синхронизируются. В строго последовательном дополнении синхронизация объектов реализуется именно в запуске метода. Однако во много текущей системе объекты требуют более утонченной схемы передачи сообщений, чтобы развязать проблемы взаимного исключения, типов і для параллельных систем. Активные объекты собственно выполняются как потоки, потому присутствие других активных объектов на них обычно не влияет. Если же активный объект имеет связь с пассивным, возможны следующие три подхода к синхронизации:

1. Последовательный — семантика пассивного объекта обеспечивается в присутствии лишь одного активного процесса;

2. Защищенный - семантика пассивного объекта обеспечивается в присутствии многих потоков управления, но активные клиенты должны договориться и обеспечить взаимное исключение;

3. Синхронный - семантика пассивного объекта обеспечивается в присутствии многих потоков управления; взаимное исключение обеспечивает сервер.

Агрегация объектов

В то время, как связки помечают равноправные или "клиент-серверные" отношения между объектами, агрегация описывает отношения целого и части, которые приводят к соответствующей иерархии объектов, причем, идя от целого {агрегату), мы можем прийти к его частям (атрибутам).

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

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

Объект, который является атрибутом другого объекта (агрегату), имеет связь со своим агрегатом. Через эту связь агрегат может посылать ему сообщение, но не наоборот.


 

5. Единичный и множественный полиморфизм. Реализация на C++, Common LISP.

Нужно сразу оговориться: нет никакого единичного или множественного полиморфизма. Есть полиморфизм и 3 его вида. Об этом ниже.

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

Если функция описывает разные реализации (возможно, с различным поведением) для ограниченного набора явно заданных типов и их комбинаций, это называется ситуативным полиморфизмом (ad hoc polimorphism). Ситуативный полиморфизм поддерживается во многих языках посредством перегрузки функций и методов.

Перегрузка процедур и функций — возможность использования одноимённых подпрограмм: процедур или функций в языках программирования.

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

Пример перегрузки функций в C + +

main() { cout<<volume(10);

cout<<volume(2.5,8);

cout<<volume(100,75,15);}

// volume of a cube объем куба

int volume(int s)

{ return(s*s*s);}

// volume of a cylinder лбъем цилиндра

double volume(double r,int h)

{ return(3.14*r*r*h);}

// volume of a cuboid объем параллепипеда

long volume(long l,int b,int h)

{ return(l*b*h);}

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

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

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

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

data List a = Nil | Cons a (List a)

length:: List a -> Integer

length Nil = 0

length (Cons x xs) = 1 + length xs

map:: (a -> b) -> List a -> List b

map f Nil = Nil

map f (Cons x xs) = Cons (f x) (map f xs)

Параметрический полиморфизм также доступен в некоторых императивных (в частности, объектно-ориентированных) языках программирования, где для его обозначения обычно используется термин «обобщённое программирование»:

В языке C++ обобщённое программирование основывается на понятии «шаблон», обозначаемом ключевым словом template.

В качестве примера приведём обобщённую функцию, возвращающую большее значение из двух.

// Описание шаблонной функции

template <typename T> T max(T x, T y)

{ if (x < y)

return y;

else return x;}

...

// Применение шаблонной функции

int a = max(10,15);

...

double f = max(123.11, 123.12);

...

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

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

Например, если имеются типы Number, Rational и Integer, связанные отношениями Number:> Rational и Number:> Integer, то функция, определённая на типе Number, также сможет принять на вход аргументы типов Integer или Rational, и её поведение будет идентичным.

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

Пример на С++

Например, предположим, что у вас есть базовый класс employee:

class employee

{ public:
employee(char *, char *, float);
void show_employee(void);
private:
char name[64];
char position[64];
float salary; };

Далее предположим, что вашей программе требуется класс manager, который добавляет данных в класс employee:

class manager: public employee

{ public:
manager(char *, char *, char *, float, float, int);
void show_manager(void);
private:
float annual_bonus;
char company_car[64];
int stock_options;};

6. Инкапсуляция и наследование. Реализация на C++, Common LISP.

Инкапсуляция

Инкапсуляция (encapsulation) - это механизм, который объединяет данные и код, манипулирующий этими данными, а также защищает и то, и другое от внешнего вмешательства или неправильного использования. В объектно-ориентированном программировании код и данные могут быть объединены вместе; в этом случае говорят, что создаётся так называемый "чёрный ящик". Когда коды и данные объединяются таким способом, создаётся объект (object). Другими словами, объект - это то, что поддерживает инкапсуляцию.

Внутри объекта коды и данные могут быть закрытыми (private). Закрытые коды или данные доступны только для других частей этого объекта. Таким образом, закрытые коды и данные недоступны для тех частей программы, которые существуют вне объекта. Если коды и данные являются открытыми, то, несмотря на то, что они заданы внутри объекта, они доступны и для других частей программы. Характерной является ситуация, когда открытая часть объекта используется для того, чтобы обеспечить контролируемый интерфейс закрытых элементов объекта.

На самом деле объект является переменной определённого пользователем типа. Может показаться странным, что объект, который объединяет коды и данные, можно рассматривать как переменную. Однако применительно к объектно-ориентированному программированию это именно так. Каждый элемент данных такого типа является составной переменной.

Для реализации на практике инкапсуляции в классе создаются 2 части – интерфейс и реализация. Интерфейс отражает внешнее представление объекта, описывая абстракцию поведения всех объектов данного класса. Реализация описывает представление этой абстракции и механизмы достижения желаемого поведения объекта.

Инкапсуляция — это возможность скрыть внутреннее устройство объекта от его пользователей, предоставив через интерфейс доступ только к тем членам объекта, с которыми клиенту разрешается работать напрямую. Необходимо пояснить разницу между абстрагированием и икапсуляцией. Инкапсуляция подразумевает наличие границы между внешним интерфейсом класса (открытыми членами, видимыми пользователям класса) и деталями его внутренней реализации. Преимущество инкапсуляции для разработчика в том, что он может открыть те члены класса, которые будут оставаться статичными, или неизменяемыми, скрыв внутреннюю организацию класса, более динамичную и в большей степени подверженную изменениям. В С# и С++ инкапсуляция достигается путем назначения члену класса модификатора доступа —

public – открытая часть, видимая всем клиентам;

private – закрытая часть, видима только самому классу и его друзьям;

protected – защищенная часть, видимая самому классу, его подклассам (классы-наследники) и друзьям.

Примеры

С++

class A{ public: int a, b; //данные открытого интерфейса int ReturnSomething(); //метод открытого интерфейса private: int Aa, Ab; //скрытые данные void Do_Something();}; //скрытый метод

Класс А инкапсулирует свойства Aa, Ab и метод Do_Something(), представляя внешний интерфейс ReturnSomething, a, b.

Наследование

Наследование – один из важнейших механизмов объектно-ориентированного программирования (наряду с инкапсуляцией, полиморфизмом и абстракцией), позволяющий описать новый класс на основе уже существующего (родительского), при этом свойства и функциональность родительского класса заимствуются новым классом. Другими словами, класс-наследник реализует спецификацию уже существующего класса (базовый класс). Это позволяет обращаться с объектами класса-наследника точно так же, как с объектами базового класса. Простое наследование Класс, от которого произошло наследование, называется базовым или родительским (англ. base class). Классы, которые произошли от базового, называются потомками, наследниками или производными классами (англ. derived class). В некоторых языках используются абстрактные классы. Абстрактный класс — это класс, содержащий хотя бы один абстрактный метод, он описан в программе, имеет поля, методы и не может использоваться для непосредственного создания объекта. То есть от абстрактного класса можно только наследовать. Объекты создаются только на основе производных классов, наследованных от абстрактного. Например, абстрактным классом может быть базовый класс «сотрудник вуза», от которого наследуются классы «аспирант», «профессор» и т. д. Так как производные классы имеют общие поля и функции (например, поле «год рождения»), то эти члены класса могут быть описаны в базовом классе. В программе создаются объекты на основе классов «аспирант», «профессор», но нет смысла создавать объект на основе класса «сотрудник вуза».

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

Примеры:

Наследование в C++:

class A{ }; //базовый классclass B: public A{ }; //public наследованиеclass C: protected A{ }; //protected наследованиеclass Z: private A{ }; //private наследование

В C++ существует три типа наследования: public, protected, private. Спецификаторы доступа членов базового класса меняются в потомках следующим образом:

Если класс объявлен как базовый для другого класса со спецификатором доступа public, тогда public члены базового класса доступны как public члены производного класса, protected члены базового класса доступны как protected члены производного класса.

Если класс объявлен как базовый для другого класса со спецификатором доступа protected, тогда public и protected члены базового класса доступны как protected члены производного класса.

Если класс объявлен как базовый для другого класса со спецификатором доступа private, тогда public и protected члены базового класса доступны как private члены производного класса.

Одним из основных преимуществ public-наследования является то, что указатель на классы-наследники может быть неявно преобразован в указатель на базовый класс, то есть для примера выше можно написать:

A* a = new B();

Эта интересная особенность открывает возможность динамической идентификации типа.

Предположим, что вашей программе требуется класс manager. Следующая программа иллюстрирует использование наследования в C++, создавая класс manager из базового класса employee:

class employee

{ public:

employee(char *, char *, float);

void show_employee(void);

private:

char name [ 64 ];

char position[64];

float salary; };

employee::employee(char *name, char *position,float salary)

{//конструктор}

void employee::show_employee(void)

{//вывод на экран}

class manager: public employee

{ public:

manager(char *, char *, char *, float, float, int);

void show_manager(void);

private:

float annual_bonus;

char company_car[64];

int stock_options; };

manager::manager(char *name, char *position, char *company_car, float salary, float bonus, int stock_options): employee(name, position, salary)

{//конструктор}

void manager::show_manager(void)

{//вывод на экран}

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...