C++. Бархатный путь. Часть 2
Страница 25. Виртуальные функции. Часть 2


 

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

pObj->A::Fun1(1);

В этом операторе мы отказываемся от услуг таблицы виртуальных функций. При этом мы сообщаем транслятору о намерении вызвать функцию-член базового класса. Механизм поддержки виртуальных функций строг и очень жёстко регламентирован. Указатель на таблицу виртуальных функций обязательно включается в самый "верхний" базовый фрагмент объекта производного класса. В таблицу указателей включаются адреса функций-членов фрагмента самого "нижнего" уровня, содержащего объявления этой функции.

Мы в очередной раз модифицируем объявление классов A, AB и объявляем новый класс ABC.

Модификация классов A и AB сводится к объявлению в них новых функций-членов:

class A
{
public:
virtual int Fun1(int key);
virtual int Fun2(int key);
};
:::::
int A::Fun2(int key)
{
cout << " Fun2( " << key << " ) from A " << endl;
return 0;
}
class AB: public A
{
public:
int Fun1(int key);
int Fun2(int key);
};
:::::
int AB::Fun2(int key)
{
cout << " Fun2( " << key << " ) from AB " << endl;
return 0;
}
Класс ABC является производным от класса AB:
class ABC: public AB
{
public:
int Fun1(int key);
};
int ABC::Fun1(int key)
{
cout << " Fun1( " << key << " ) from ABC " << endl;
return 0;
}

В этот класс входит объявление функции-члена Fun1, которая объявляется в косвенном базовом классе A как виртуальная функция. Кроме того, этот класс наследует от непосредственной базы функцию-член Fun2. Эта функция также объявляется в базовом классе A как виртуальная. Мы объявляем объект-представитель класса ABC:

ABC MyABC;

Его схему можно представить следующим образом:

MyABC::=
vptr
A
AB
ABC
vtbl::=
&AB::Fun2
&ABC::Fun1

Таблица виртуальных функций сейчас содержит два элемента. Мы настраиваем указатель на объект базового класса на объект MyABC, затем вызываем функции-члены:

pObj = &MyABC;
pObj->Fun1(1);
pObj->Fun2(2);

В этом случае невозможно вызвать функцию-член AB::Fun1(), поскольку её адрес не содержится в списке виртуальных функций, а с верхнего уровня объекта MyABC, на который настроен указатель pObj, она просто не видна. Таблица виртуальных функций строится конструктором в момент создания объекта соответствующего объекта. Безусловно, транслятор обеспечивает соответствующее кодирование конструктора. Но транслятор не в состоянии определить содержание таблицы виртуальных функций для конкретного объекта. Это задача времени исполнения. Пока таблица виртуальных функций не будет построена для конкретного объекта, соответствующая функция-член производного класса не сможет быть вызвана. В этом легко убедиться, после очередной модификации объявления классов.

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

#include <iostream.h>
class A
{
public:
virtual int Fun1(int key);
};
int A::Fun1(int key)
{
cout << " Fun1( " << key << " ) from A." << endl;
return 0;
}
class AB: public A
{
public:
AB() {Fun1(125);};
int Fun2(int key);
};
int AB::Fun2(int key)
{
Fun1(key * 5);
cout << " Fun2( " << key << " ) from AB." << endl;
return 0;
}
class ABC: public AB
{
public:
int Fun1(int key);
};
int ABC::Fun1(int key)
{
cout << " Fun1( " << key << " ) from ABC." << endl;
return 0;
}
void main ()
{
ABC MyABC; // Вызывается A::Fun1().
MyABC.Fun1(1); // Вызывается ABC::Fun1().
MyABC.Fun2(1); // Вызываются AB::Fun2() и ABC::Fun1().
MyABC.A::Fun1(1); // Вызывается A::Fun1().
A *pObj = &MyABC; // Определяем и настраиваем указатель.
cout << "==========" << endl;
pObj->Fun1(2); // Вызывается ABC::Fun1().
//pObj->Fun2(2); // Эта функция через указатель недоступна !!!
pObj->A::Fun1(2); // Вызывается A::Fun1().
}

 
Следующая статья »