7.2 Абстрактные классы
---------------------
Если базовый класс используется только для порождения производных классов, то виртуальные функции в базовом классе могут
быть "пустыми", поскольку никогда не будут вызваны для объекта
базового класса. Такой базовый класс называется абстрактным. Виртуальные функции в определении класса обозначаются следующим образом:
class base
{
public:
virtual print() =0;
virtual get() =0;
}
Естественно, что определять тела этих функций не требуется.
7.3 Множественное наследование и виртуальные функции
---------------------------------------------------
Множественным наследованием называется процесс создания производного класса из двух и более базовых. В этом случае производный класс наследует данные и функции всех своих базовых классов.
Существенным для реализации множественного наследования является
то, что адреса объектов второго и т.д. базовых классов не совпадают с адресом объекта производного и первого базового классов,
то есть имеют фиксированные смещения относительно начала объекта:
class d : public a,public b, public c { };
d D1;
pd = &D1; // #define db sizeof(a)
pa = pd; // #define dc sizeof(a)+sizeof(b)
pb = pd; // pb = (char*)pd + db
pc = pd; // pc = (char*)pd + dc
D1
pd -------------------->-d---------¬
pa --------------------->-a-------¬¦T T
¦¦ ¦¦¦ ¦ db = sizeof(a)
¦L---------¦¦ +
pb --------------------->-b-------¬¦¦ dc = sizeof(a) + sizeof(b)
¦L---------¦¦
pc --------------------->-c-------¬¦+
¦L---------¦
¦ ¦
L----------
Преобразование ссылки на объект производного класса к ссылке
на объект базового класса требует добавления к указателю текущего
объекта this соответствующего смещения (db,dc), обратное преобразование - вычитание этого же смещения. Такое действие выполняется
компилятором, когда в объекте производного класса наследуется
функция из второго и т.д. базового класса, например при определении в классе "b" функции "f()" и ее наследовании в классе "d" вызов D1.f() будет реализован следующим образом:
this = &D1; // Адрес объекта производного класса
this = (char*)this + db // Адрес объекта класса b в нем
b::f(this); // Вызов функции в классе b со своим
// объектом
Рассмотрим особенности механизма виртуальных функций при
множественном наследовании. Во-первых, на каждый базовый класс в
производном классе создается своя таблица виртуальных функций (в
нашем случае - для "a" в "d", для "b" в "d" и для "c" в "d").
Во-вторых, если функция базового класса переопределена в производном, то при вызове виртуальной функции требуется преобразовать
ссылку на объект базового класса в ссылку на объект производного,
то есть для второго и т.д. базовых классов вычесть из this соответствующее смещение. Для этого транслятор включает соответствующий код, корректирующий значение this в виде "заплаты", передающей управление командой перехода к переопределяемой функции.
class a
{
public: virtual void f();
virtual void g();
};
class b
{
public: virtual void h();
virtual void t();
};
class c : public a, public b
{ // f(),t() наследуются
public: void g(); // g() переопределяется
void h(); // h() переопределяется
}
a A1;
b B1;
c C1;
pa = &A1;
pb = &B1;
pa->f(); // Вызов a::f()
pb->h(); // Вызов b::h()
pa = &C1;
pb = &C1;
pa->f(); // Вызов a::f()
pa->g(); // Вызов c::g()
pb->h(); // Вызов c::h()
pb->t(); // Вызов b::t()
Таблицы виртуальных функций для данного примера имеют вид:
A1
-a----¬ Таблица ВФ для "a"
¦ ------------>--------¬
+-----+ ¦a::f() ¦
L------ +-------+
¦a::g() ¦
L------- B1
-b----¬ Таблица ВФ для "b"
¦ ------------>--------¬
+-----+ ¦b::h() ¦
L------ +-------+
¦b::t() ¦
L------- C1
T --c-----¬ Таблица ВФ для "a" в "c"
¦ ¦--a---¬¦ --------¬
db ¦ ¦¦ ----------->¦a::f() ¦
¦ ¦L------¦ +-------+
+ ¦--b---¬¦ ¦c::g() ¦
¦¦ -------¬ L------- ¦L------¦ ¦ Таблица ВФ для "b" в "c"
¦ ¦ ¦
¦ ¦ L--->--------¬ "Заплата" для c::h()
L-------- ¦ xxx()----->--xxx()----------------¬
+-------+ ¦ this=(char*)this - db¦
¦b::t() ¦ ¦ goto c::h ¦
L-------- L----------------------
Другим вариантом решения проблемы является хранение необходимых смещений в самих таблицах виртуальных функций.
7.4. Виртуальные базовые классы
------------------------------
В процессе иерархического определения производных классов
может получиться, что в объект производного класса войдут
несколько объектов базового класса, например
class base {}
class a : public base {}
class b : public base {}
class c : a, b {}
В классе "c" присутствуют два объекта класса base. Для исключения такого дублирования объект базового класса должен быть
объявлен виртуальным
class a : virtual public base {}
class b : virtual public base {}
class c : public a, public b {}
a A1;
b B1;
c C1;
Объект обыкновенного базового класса располагается, как правило, в начале объекта производного класса и имеет фиксированное
смещение. Если же базовый класс является виртуальным, то требуется его динамическое размещение. Тогда в объекте производного
класса на соответствующем месте размещается не объект базового
класса, а ссылка на него, которая устанавливается конструктором.
Для вышеприведенного примера имеем
A1 B1 C1
--a------¬ --b-----¬ --c---------------¬
¦ ------¬ ¦ ------¬ ¦ --a-------¬ ¦
+--------+ ¦ +-------+ ¦ ¦ ¦ -------¬ ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦ +---------+ ¦ ¦
¦-base--¬---------¬0 integer
+--------+ +--------+.. real
+--------+ +--------+j --dat-------¬
base** ¦ -------------->-base-----¬¦
+--------+ ¦L----------¦
base* ¦ ¦
L----------- base
head Строка заголовка БД
--¬ S0
¦-------------->---------¬0 -string---¬
L-- ¦ ------------------>-base---¬¦
base** +--------+ ---------->L--------¦
¦ --------------¬ L--------- +--------+ ¦ ¦ D0
¦ --------- ¦ -dat------¬
+--------+ L--->-base---¬¦
base* ¦L--------¦
L---------//------------------------------------------------------// Меню классов объектов (типов столбцов)
string S0;
dat D0;
time T0;
integer I0;
base *TYPE[] = {
(base*) &S0;
(base*) &D0;
(base*) &T0;
(base*) &I0;
};
//-----------------------------------------------------// Создание структуры БД
#define MAXCOL 30
#define MAXREC 1000
table::table()
{
int i,j,n;
char ss[80];
names = new char*[MAXCOL]; // Таблица адресов имен столбцов
head = new base*[MAXCOL]; // Таблица ссылок на объекты
for (nc=0; nc
... обучающих программ Обучающие программы, построенные на бихевиористской основе, подразделяют на: а) линейные, разработанные Скиннером, б) разветвленные программы Н. Краудера. 1. Линейная система программированного обучения, первоначально разработанная американским психологом Б. Скиннером в начале 60-х гг. ХХ в. на основе бихевиористского направления в психологии. · Он выдвинул следующие ...
... и общества. Поэтому сознательное поддержание равновесия между естественной и социальной системами, сохранение их целостности возможно только в том случае, если в системе социального управления будет по возможности полнее отражаться многообразие свойств человека, вытекающее из богатства его природы. При этом социальное управление должно быть ориентировано на развитие человеческой индивидуальности, ...
... . Наложение волн разной длины и размаха колебания создает достаточно пеструю картину развития, которую трудно уложить в единую схему. В целом социальное программирование должно опираться на анализ тенденций и закономерностей социального развития, учитывать противоречивый, многоплановый, многоуровневый характер социальных процессов, их стохастическую природу и сложный механизм реализации. ...
... отбора. ГЛАВА II. ЦЕЛЬ, ЗАДАЧИ, МЕТОДЫ И ОРГАНИЗАЦИЯ ИССЛЕДОВАНИЯ 2.1 Цель и задачи исследования Цель исследования - повышение эффективности физической подготовки вратарей учебно-тренировочных групп на соревновательном этапе. В процессе реализации поставленной цели решались следующие основные задачи: 1. Изучить особенности возрастного развития двигательных способностей футболистов ...
0 комментариев