Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP / lectures / 2_oop.ppt
Скачиваний:
56
Добавлен:
03.03.2016
Размер:
495.1 Кб
Скачать

Пример. Использование указателей на объекты. Приведение типов.

class Animal { protected:

char *pname; public:

Animal(char *AnName) {

pname=new char[strlen(AnName)+1]; strcpy(pname, AnName);

}

virtual char* speak() {return "Не известно";} virtual char *name() {return pname;}

};

class Dog: public Animal

{

public:

Dog(char *name):Animal(name){} char * speak() {

char *phrase; phrase=strdup(pname);

return strcat(phrase," говорит Гав!");

}

 

virtual char * sit() {

 

char *phrase;

 

phrase=strdup(pname);

 

return strcat(phrase," сидит");

}

};

int main()

{

Animal* p[2]={new Animal("a"), new Dog("Шарик")};

printf("%s\n",p[0]->speak()); printf("%s\n",p[1]->speak()); //printf("%s\n",p[1]->sit()); //ошибка

компиляции: 'sit' : is not a member of 'Animal' printf("%s\n",((Dog*)p[1])->sit());

return 0;

}

Механизм работы позднего связывания

Компилятор для каждого класса (не объекта),

содержащего хотя бы один виртуальный метод, создает таблицу виртуальных функций VFTABLE.

В эту таблицу помещаются адреса виртуальных функций класса в порядке их описания.

Адрес любого виртуального метода имеет в VFTABLE одно и то же смещение для каждого класса в пределах иерархии.

В каждом классе также помещается указатель VFPTR на таблицу виртуальный функций.

Схема работы с VFTABLE для объектов из предыдущего примера:

 

Объект класса Animal

[0]

&Animal::speak()

P

vfptr

[1]

&Animal::name()

P0

 

[0]

&Dog::speak()

 

 

 

P1

Объект класса Dog

[1]

&Animal::name()

vfptr

[2]

&Dog::sit()

 

 

 

 

При явном связывании мы получаем

абсолютные адреса функций.

При неявном связывании, вызывая функцию

name(), говорят, что надо вызвать функцию по адресу VFPTR+1.

Наследование и таблица виртуальных функций

&Animal::speak () &Dog::speak() &Animal::name () &Animal::name()

&Dog::sit()

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

Так как у базового класса Animal нет функции

sit(), то компилятор выдает ошибку при попытке ее использования (p[1]->sit()).

Чтобы для p[1] вызвать функцию sit(), необходимо использовать явное приведение типов:

((Dog*) p[1])->sit();

Абстрактные базовые классы

Класс становится абстрактным, когда в него добавляется чистая виртуальная функция:

virtual void f() = 0;

Нельзя создавать объекты абстрактного базового класса.

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

Пример. Работа с абстрактным классом

class Animal { protected:

char *pname; public:

Animal(char *AnName)

{

pname=new char[strlen(AnName)+1]; strcpy(pname, AnName);

}

virtual char* speak()=0; virtual char *eat()=0;

virtual char *name() {return pname;}

};

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

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

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

char* Animal::speak() {return "Animal::speak()";}

char* Animal::eat() {return "Animal::eat()";}

Соседние файлы в папке lectures