Помощничек
Главная | Обратная связь


Археология
Архитектура
Астрономия
Аудит
Биология
Ботаника
Бухгалтерский учёт
Войное дело
Генетика
География
Геология
Дизайн
Искусство
История
Кино
Кулинария
Культура
Литература
Математика
Медицина
Металлургия
Мифология
Музыка
Психология
Религия
Спорт
Строительство
Техника
Транспорт
Туризм
Усадьба
Физика
Фотография
Химия
Экология
Электричество
Электроника
Энергетика

Class VECTOR: public MATRIX, public DARRAY



{ ............................;

};

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

Что наследуется в производном классе

Производный класс наследует:

- компоненты;

- функции;

- перегруженные операции (за исключением некоторых);

- статические компоненты и функции;

- дружественность к внешним функциям.

Статические компоненты наследуются и действуют в каждом из производных классов независимо. Для каждого из производных классов создаются свои статические компоненты и статические функции.

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

Производный класс НЕ наследует конструкторы и деструкторы.

Перегруженная операция присваивания наследуется наполовину. Пусть Bas и Der - имена базового и производного классов. И пусть Bas содержит перегруженную операцию присваивания, а Der - не содержит. Тогда присваивания вида

Bas = Bas

Bas = Der

будут компилироваться и выполняться правильно, а операции вида

Der = Der

будет компилироваться, но не всегда будет выполняться правильно. А операция вида

Der = Bas

будут порождать сообщение об ошибке уже на этапе компиляции.

Отсюда следует:

1) если параметры некоторой функции имеют тип базового класса, то фактические параметры могут иметь тип производного класса;

2) операцию присваивания в производном классе необходимо перегружать в любом случае, как для динамических, так и для не динамических классов.

Работа конструкторов и деструкторов

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

class Der: public Bas

{ ............................;

Der(char* p, int a): Bas(p,a) {}

............................;

};

 

Конструктор Der имеет пустое тело, его работа состоит в том, что он передает параметры p,a конструктору базового класса Bas.

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

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

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

· Если в производном классе деструктор не описан, он формируется по умолчанию и вызывает деструкторы всех базовых классов.

· В отличие от конструкторов, при написании деструктора производного класса в нем не требуется явно вызывать деструкторы базовых классов, поскольку это будет сделано автоматически.

· Для иерархии классов, состоящей из нескольких уровней, деструкторы вызываются в порядке, обратном вызову конструкторов: вначале вызывается деструктор самого класса, затем деструкторы элементов класса, потом деструкторы базовых классов.

Атрибуты доступа

Перед именем базового класса в заголовке производного класса необходимо указать один из атрибутов доступа - public, protected или private. Он управляют bpvtytybtv атрибутов доступа при наследовании. Например:

class VECTOR: public MATRIX, private SCALAR

{ ..........................................;

};

По умолчанию для классов, объявляемых как class, атрибутом наследования является private, а для классов, объявляемых как struct, атрибутом наследования является public. При наследовании действуют следующие правила.

1. private-компоненты и функции базового класса в производном классе становятся недоступными вне зависимости от атрибута наследования. Доступ к ним возможен только через методы базового класса или через функцию, объявленную в базовом классе как friend-функция.

2. Если базовый класс наследуется с атрибутом private, то все его доступные компоненты приобретают атрибут private, независимо от собственного атрибута.

3. Если базовый класс наследуется с атрибутом protected, тогда все его доступные компоненты приобретают атрибут prjtected, независимо от собственного атрибута.

4. Если базовый класс наследуется с атрибутом public, то все его доступные компоненты наследуются без изменения собственного атрибута.

Работа указателей в иерархии наследования

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

class Alfa

{ ............

void draw();

};

Пусть p - указатель на объект класса Alfa:

Alfa* p = new Alfa;

Рассмотрим вызов метода draw, который записан следующим образом:

p->draw();

При выполнении этого оператора будет вызван метод draw объекта класса Alfa с адресом p. То есть вызывается метод того класса, который соответствует типу указателя p. Не типом объекта, для которого вызывается метод, а типом указателя! Ссылка на метод draw разрешается в этом случае во время компиляции программы. Этот процесс называется ранним связыванием.

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

p->draw();

означает, что необходимо построить вызов того метода draw, который соответствует типу объекта с адресом p, а не типу базового класса. Но значение указателя p может формироваться во время выполнения программы. Это означает, что вызов метода draw можно реализовать только во время выполнения программы. Механизм реализации вызова метода, который используется в таких случаях, называется поздним связыванием.

Виртуальные функции

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

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

virtual void fun(int) = 0 ;

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

Правила описания и использования виртуальных методов.

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

· Виртуальные методы наследуются, т.е. переопределять их в производном классе требуется только при необходимости указать иной алгоритм метода. Атрибут доступа при переопределении не изменяется.

· Если виртуальный метод переопределен в производном классе, объекты этого класса могут получить доступ к соответствующему методу базового класса с помощью операции доступа к пространству базового класса "::" .

· Виртуальный метод не может объявляться с модификатором static.

· Виртуальный метод может быть объявлен как дружественный..

Механизм вызова виртуальной функции можно отключить, указав в вызове ее имя базового класса:

pa->sum(); // механизм вызова вирт. функции работает

pa->Base::sum(); // механизм вызова вирт. функции не работает

Абстрактные классы и чистые функции

Идея наследования не была бы полной, если бы не было чистых виртуальных функций и абстрактных классов.

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

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

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

· Объявление указателей и ссылок на абстрактный класс допускается, однако инициализация с созданием временного объекта не разрешается.

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

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

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

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

Множественное наследование порождает некоторые проблемы. Если в базовых классах есть одноименные объекты, может проявляться конфликт идентификаторов. Устраняется конфликт с помощью операции доступа к области видимости.

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

В иерархии с множественным наследованием производный класс может косвенно наследовать несколько экземпляров базового класса. При компиляции в этом случае будет выдано сообщение о неоднозначности обращений к компонентам класса. Устранить неоднозначность можно указав имя соответствующего базового класса: например, вместо d.func(); записать d.B::func();

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

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

Иерархия классов с простым наследованием представляет собой структуру типа "дерево", иерархии с множественным наследованием соответствует структура типа "граф".

 

 




Поиск по сайту:

©2015-2020 studopedya.ru Все права принадлежат авторам размещенных материалов.