Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
методичка по программированию СУА.doc
Скачиваний:
12
Добавлен:
11.11.2019
Размер:
1.3 Mб
Скачать

Виртуальные методы и переопределение методов

Что бы изменилось, если бы метод Name не был описан как виртуальный? В таком случае решение о том, какой именно метод будет выполняться, принимается статически, во время компиляции программы. В примере с методом Name, поскольку мы работаем с указателем на базовый класс, был бы вызван метод Name класса Item. При определении метода как virtual решение о том, какой именно метод будет выполняться, принимается во время выполнения.

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

func(Item item)

{

item.Name();

}

func1(Item& item)

{

item.Name();

}

// вызывается метод Item::Name()

// вызывается метод в соответствии

// с типом того объекта, на который

// ссылается item

Преобразование базового и производного классов

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

Circle* pC;

. . .

Shape* pShape = pC;

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

Item* iPtr;

. . .

Book* bPtr = (Book*)iPtr;

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

Внутреннее и защищенное наследование

До сих пор мы использовали только внешнее наследование. Однако в языке Си++ имеется также внутреннее и защищенное наследование. Если перед именем базового класса ставится ключевое слово private, то наследование называется внутренним.

class B : private A

{

. . .

};

В случае внутреннего наследования внешняя и защищенная части базового класса становятся внутренней частью производного класса. Внутренняя часть базового класса остается для производного класса недоступной.

Если перед именем базового класса поставить ключевое слово protected, то будет использоваться защищенное наследование. При нем внешняя и защищенная части базового класса становятся защищенной частью производного класса. Внутренняя часть базового класса остается недоступной для производного класса.

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

Если в классе A был определен какой-то метод:

class A

{

public:

int foo();

};

то запись

B b;

b.foo();

недопустима, так же, как и наследуемый от B

class C

{

int m() {

foo();

}

};

если класс B внутренне наследует A. Если же класс B использовал защищенное наследование, то первая запись b.foo() также была бы неправильной, но зато вторая была бы верна.