Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Конференция «Технологии управления данными 2018»
СУБД, платформы, инструменты, реальные проекты.
29 ноября 2018 г.

Виртуальные классы

Мы продолжаем модификацию последнего варианта нашей программы, добавляя к прототипу функции int A::Fun1(int); спецификатор virtual.

class A
{
public:
int x0;
virtual int Fun1(int key);
};

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

Как бы мы ни старались, вызвать функцию-член класса A из любого фрагмента объекта-представителя производного класса D невозможно. Сначала конструкторы строят объект, настраивают таблицы виртуальных функций, а потом уже мы сами начинаем его "перекраивать", создавая на основе базового фрагмента видимость самостоятельного объекта. Напрасно. Объект построен, таблицы виртуальных функций также настроены. До конца жизни объекта виртуальные функции остаются недоступны.

Следует обратить особое внимание на то обстоятельство, что независимо от места вызова виртуальной функции (а мы её вызываем непосредственно из базовых фрагментов объекта), замещающей функции в качестве параметра передаётся корректное значение this указателя.

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

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

В этом разделе нам осталось обсудить понятие виртуального базового класса. Согласно соответствующей БНФ, спецификатор virtual может быть включён в описатель базы:

ОписательБазы ::= ПолноеИмяКласса
              ::= [virtual] [СпецификаторДоступа] ПолноеИмяКласса
              ::= [СпецификаторДоступа] [virtual] ПолноеИмяКласса

Модифицируем нашу программу. Мы добавим в описатели баз производных классов B и C спецификатор virtual:

class A
{
public:
int x0;
int Fun1(int key);
};
class B: virtual public A
{
public:
int x0;
int Fun1(int key);
int Fun2(int key);
};
class C: public virtual A
{
public:
int x0;
int Fun2(int key);
};
class D: public B, public C
{
public:
int x0;
int Fun1(int key);
};

Вот как выглядит после модификации граф производного класса D:

                             A
               B                          C
                             D

А вот как меняется структура класса D, представляемая в виде неполной схемы. Спецификатор virtual способствует минимизации структуры производного класса. Виртуальные базовые классы не тиражируются.

A
B
C
D

А вот и схема объекта-представителя класса D.

D MyD;
MyD ::=
A
	(int)x0;
	(int)xA;
B
	(int)xB;
C
	(int)x0;
D
	(int)x0;
	(int)xD;

Спецификатор virtual в описании базы позволяет минимизировать структуру объекта. Различные варианты обращения к данным-членам базового фрагмента приводят к модификации одних и тех же переменных.

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

В C++ допускаются такие варианты объявления производных классов, при которых одни и те же классы одновременно выступают в роли виртуальных и невиртуальных базовых классов. Например, сам класс D может быть использован в качестве базового класса:

class F: public A
{
:::::
}
class G: public A, public D
{
:::::
}
:::::
G MyG;

Множество одноименных виртуальных и невиртуальных базовых фрагментов, данных-членов, простых и виртуальных функций. Можно до бесконечности рисовать направленные ациклические графы, диаграммы классов и объектов… Искусство объявления классов и навигации по фрагментам объектов совершенствуется в результате напряжённой длительной практики.

Введём новое понятие, связанное с доступом к данным и функциям-членам производных классов.

Имя x (имя переменной, класса, или функции), объявленное в классе X, обозначается как X::x. Имя B::f доминирует над именем A::f, если объявление класса B содержит в списке баз имя класса A.

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

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

Назад | Содержание | Вперед

 

Новости мира IT:

Архив новостей

Последние комментарии:

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 985 1945361
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2015 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...