- •Введение. Принципы объектно-ориентированного программирования
- •Глава 1. Классы и объекты
- •1.1. Операция разрешения области видимости ::
- •1.2. Перечислимый тип
- •1.3. Модификатор const
- •1.4. Новый тип данных – ссылка &
- •Inline определение_функции
- •2. Определение класса. Сокрытие информации.
- •3. Объект.
- •4. Конструкторы и деструкторы
- •4.1.Назначение конструктора
- •4.2. Конструктор копирования
- •X::X(X&); // где X – имя класса
- •4.3. Деструктор
- •5. Неявный указатель this
- •6. Перегрузка операций
- •7. Примеры перегрузки некоторых операций
- •7.1. Перегрузка операции [ ]
- •7.2. Перегрузка операции ()
- •7.6. Перегрузка операции (тип)
- •8. Дружественность
- •Istream
- •10. Массивы объектов.
- •11. Функции- и классы-шаблоны
- •11.1 Функции-шаблоны (родовые функции)
- •11.2 Классы-шаблоны
- •12. Член-данные класса – объекты другого класса: агрегированные классы.
- •Глава 2. Наследование. Полиморфизм
- •1. Базовый и порожденный классы
- •2. Конструкторы порожденного класса
- •3. Стандартные преобразования при наследовании
- •4. Множественное наследование. Виртуальный базовый класс
- •4.1. Прямые базовые классы
- •4.2. Виртуальный базовый класс
- •5. Полиморфизм, раннее и позднее связывание, виртуальные функции
- •5.1 Раннее (статическое) и позднее (динамическое) связывание
- •5.2. Определение виртуальной функции
- •5.3. Чистая виртуальная функция и абстрактный класс
- •5.4. Правила определения виртуальных функций
- •5.5. Механизм позднего связывания
- •6. Библиотека fstream – работа с файлами
- •Глава 3. Библиотека стандартных шаблонов (бсш). Контейнеры
- •1. Контейнер. Структура бсш.
- •2. Контейнер Vector – динамический массив
- •Контейнер list – список
- •4. Контейнер Set – множество
- •Содержание
- •Глава 1. Классы и объекты
- •Глава 2. Наследование. Полиморфизм
- •Глава 3. Библиотека стандартных шаблонов (бсш). Контейнеры
5.5. Механизм позднего связывания
При использовании виртуальных функций для каждого класса заводится своя таблица виртуальных функций Vtab, в каждой строке которой находится указатель на виртуальную функцию данного класса (т.е. адрес ее начала в ОП). В каждом объекте базового и порожденного класса хранится адрес начала этой таблицы Vptr сразу же за член-данными базовой части объекта. При вызове виртуальной функции ее адрес A(vf) вычисляется по формуле
A(vf) = ((Vptr) + смещение в Vtab для этой функции).
Расмотрим схему.
class A
{int a, b;
public:
virtual void F1( ) {...}
virtual void F2( ) {...}
..};
class B: public A
{ int a, b;
public:
virtual void F1( ) {..}
virtual void F2( ) {..}
..};
class C: public B
{int a, b;
public:
virtual void F1( ) {...}
virtual void F2( ) {...}
..};
void main( )
{A x, *pa; B y(1, 2, 3, 4); C z(6, 5, 4, 3, 2, 1);
pa = &z; pa –> F1( );...
}
Когда объявляется указатель A *pa, то его значение неопределено. При выполнении оператора pa = &z значение указателя pa становится равным адресу объекта z. Поэтому при вызове pa –> F1( )
адрес функции F1( ) = (pa –> Vptr + 0) = (VtabC + 0 )
и, следовательно, будет вызвана функция F1( ) класса C. Заметим еще раз, что адрес функции F1( ) всегда определяется по одному правилу в любом месте виртуального вызова, но меняется за счет разного значения ячейки Vptr у разных объектов. Таким образом, вычисление адреса виртуальной функции компилятор задает программно, но ... если мы скажем волшебное слово virtual. Если функции не виртуальные, то Vtab и Vptr не формируются. Поэтому, если функции имеют одинаковое имя, но не объявлены виртуальными, берется ближайшая функция из класса для данного типа указателя (по правилу доминирования).
Пример виртуальной функции, рисующей квадрат при нажатии ‘q’, круг при нажатии ‘c’, треугольник при нажатии ‘t.’ Перед этим 100 раз «падают » звездочки ‘*’.
// Абстрактный базовый класс – фигура
class Shape
{protected: int x, y;
public:
Shape(int xx = 0, int yy = 0) {x = xx; y = yy;}
virtual void Draw(int) = 0; // Чистая виртуальная
};
// Порожденный класс – квадрат
class Square: public Shape
{int l;
public:
Square(int x, int y, int ll): Shape(x, y), l(ll) { }
void Draw(int c) { … }
};
// Порожденный класс – круг
class Circle: public Shape
{int r;
public:
Circle(int x, int y, int rr): Shape(x, y), r(rr) { }
void Draw(int c) { … }
};
// Порожденный класс – “звезда” *
class Star: public Shape
{public:
Star(int xx, int yy): Shape(xx, yy) { }
void Draw(int c) { … }
};
// Порожденный класс – треугольник
class Triang: public Shape
{int l;
int mas[8]; // координаты вершин тр-ка
public:
Triang(int , int , int ); // конструктор
void Draw(int c) { … }
};
Triang:: Triang(int xx, int yy, int ll): Shape(xx, yy)
{int i; l = ll;
float r, a, da; // a – угол, da – приращение угла
r = l / sqrt(3); da = 6.28 / 3;
for(i = 0, a = 3.14 / 2; i < 6; a = a + da)
{ mas[i++] = x + int(r * cos(a));
mas[i++] = y - int(r * sin(a));
}
mas[i++] = mas[0];
mas[i] = mas[1];
}
void main( )
{Shape *psh; // Указатель на базовый класс
int i, k; char s;
// определяем 3 объекта
Square q(300, 200, 100); // квадрат
Circle c(300, 100, 50); // круг
Triang t(300, 100, 100); // треугольник
for(i = 0; i < 100; i++)
{psh = new Star(random(640) + 2, random(400) + 2);
psh –> Draw(random(15) + 1); // выводятся случайные '*'
delete psh;
}
cin >> s;
switch(s) // клавиша нажата, какая?
{case 's': psh = &q; break; // занести адрес объекта “квадрат”
case 'c': psh = &c; break; // занести адрес объекта “круг”
case 't': psh = &t; break; // занести адрес объекта “треугольник”
case 13: return;
default:: continue; // иначе продолжить
}
if (k = random(15) + 1) == 7) k =5;
psh –> Draw(k); // Виртуальный вызов виртуальной функции Draw( )
}
Итоговая таблица
|
Выходные параметры |
Имя функции |
Входные параметры |
Тело функции |
Перегруженная функция |
разные |
одинаковое |
разные |
разное |
Функция-шаблон |
разные |
одинаковое |
разные |
одинаковое |
Виртуальная функция |
одинаковое |
одинаковое |
Одинаковое |
разное |
