Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Вопросы для экзамена по курсу ООП.doc
Скачиваний:
4
Добавлен:
21.04.2019
Размер:
180.22 Кб
Скачать

Часть 3:

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

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

class monstr{

public: int get_health();

class hero{

public: int get_health();

}:

class ostrich: public monstr, public hero{ }:

int main(){ ostrich A;

cout « A.monstr::get_health(); cout « A.hero::get_health();

}

Как видно из примера, для вызова метода gethealth требуется явно указывать класс, в котором он описан. Использование обычной для вызова метода класса конструкции A.gethealthO приведет к ошибке, поскольку компилятор не в состоянии разобраться, к методу какого из базовых классов требуется обра­титься.

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

class monstr{ }:

class daemon: virtual public monstr{ }:

class lady: virtual public monstr{

class baby: public daemon, public lady{ }:

Класс baby содержит только один экземпляр полей класса monstr. Если базовый класс наследуется и как виртуальный, и обычным образом, в производном классе будут присутствовать отдельные экземпляры для каждого невиртуального вхож­дения и еще один экземпляр для виртуального.

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

  1. Идентификация типа во время выполнения.

  2. Средства преобразования типов.

  1. Явные преобразования static_cast, dynamic_cast.

Операция static_cast

Операция static_cast используется для преобразования типа на этапе компиля­ции между:

  • целыми типами;

  • целыми и вещественными типами;

  • целыми и перечисляемыми типами;

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

Формат операции:

Static_cast <тип> (выражение)

Результат операции имеет указанный тип, который может быть ссылкой, указа­телем, арифметическим или перечисляемым типом,

При выполнении операции внутреннее представление данных может быть моди­фицировано, хотя численное значение остается неизменным. Например:

float f - 100:

int ,i = static_cast <int> (f);

// Целые и вещественные имеют различное внутреннее представление

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

Операция static_cast позволяет выполнять преобразования из производного класса в базовый и наоборот без ограничений:

class В{};

class С: public В{}: С с:

В *bp = static_cast<B*>(c): // Производный -> базовый В b:

С &ср = static_cast<C&>(b); // Базовый -> производный

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

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

Операция dynamic_cast

Операция применяется для преобразования указателей родственных классов ие­рархии, в основном — указателя базового класса в указатель на производныйкласс, при этом во время выполнения программы производится проверка допус­тимости преобразования.

Формат операции:

dynamiccast <тип *> (выражение)

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

Повышающее преобразование

Выполнение с помощью операции dynamic_cast повышающего преобразования равносильно простому присваиванию:

class В{ /* ... */ }:

class C: public В{ /* ... */ }:

C* с = new C:

В* b = dynamic_cast<B*>(c); // Эквивалентно В* b = с:

Понижающее преобразование

Чаще всего операция dynamic_cast применяется при понижающем преобразова­нии — когда компилятор не имеет возможности проверить правильность приве­дения.

Производные классы могут содержать функции, которых нет в базовых классах. Для их вызова через указатель базового класса нужно иметь уверенность, что этот указатель в действительности ссылается на объект производного класса. Та­кая проверка производится в момент выполнения приведения типа с использова­нием RTTI (run-time type information) — «информации о типе во время выполне­ния программы». Для того чтобы проверка допустимости могла быть выполнена, аргумент операции dynamic_cast должен быть полиморфного типа, то есть иметь хотя бы один виртуальный метод (см. с. 205).

Для использования RTTI необходимо подключить к программе заголовочный файл <typeinfo>. Кроме того, необходимо, чтобы был установлен соответствующий режим ком­пилятора.

С помощью операции dynamic_cast такое преобразование возможно при условии, что класс является полиморфным и преобразование недвусмысленно. Рассмотрим пример, в котором выполняется понижающее преобразование вирту­ального базового класса:

#include <iostream.h>

#include <typeinfo.h>

class A{

public: virtual ~A(){};};

class B: public virtual A{}:

class С: public virtual A{};

class D: public B. public C{};

void demo(A *a){

D* d - dynamic_cast<D*>(a);

if (d) { ... }

}

int main(){

D *d - new D; demo(d); return 0:

}

Преобразование ссылок

Для аргумента-ссылки смысл операции преобразования несколько иной, чем для указателя. Поскольку ссылка всегда указывает на конкретный объект, операция dynaramic_cast должна выполнять преобразование именно к типу этого объекта. Корректность приведения проверяется автоматически, в случае несовпадения порождается исключение badcast:

Перекрестное преобразование

Операция dynamic_cast позволяет выполнять безопасное преобразование типа между производными классами одного базового класса.