C++. Бархатный путь. Часть 2 - Множественное наследование. Часть 2
ОГЛАВЛЕНИЕ
А теперь изменяется значение данного-члена базового фрагмента - представителя класса С. И опять же транслятор однозначно определяет местоположение изменяемой переменной. Переменная x0 была объявлена в непосредственном базовом классе C. И операция доступа указывает на эту переменную. А вот попытка изменения значения переменной x0, расположенной базовом фрагменте-представителе класса A "со стороны" непосредственного базового класса C обречена. Так, оператор
MyD.A::x0 = 777;некорректен по причине неоднозначности соотнесения класса и его члена, поскольку непонятно, о каком базовом фрагменте-представителе класса A идёт речь. Выражения доступа с составными квалифицированными именами, как например,
MyD.C::A::x0в контексте нашей программы также некорректны: составное квалифицированное имя предполагает вложенное объявление класса. Это свойство операции доступа уже обсуждалось ранее, в разделах, непосредственно посвящённых операциям. Вложенные объявления будут рассмотрены ниже.
Операция :: оставляет в "мёртвой зоне" целые фрагменты объектов. Однако возможность доступа к членам класса, которые оказались вне пределов досягаемости операции доступа всё же существует. Она обеспечивается указателями и операциями явного преобразования типа.
Идея состоит в том, чтобы, объявив указатель на объект-представитель базового класса, попытаться его настроить с помощью операций явного преобразования типа на соответствующий фрагмент объекта производного класса. В результате недосягаемые с помощью операции доступа фрагменты объекта превращаются в безымянные объекты простой конфигурации. Доступ к их членам в этом случае обеспечивается обычными операциями косвенного обращения. Рассмотрим несколько строк, которые демонстрируют такую технику работы с недосягаемыми фрагментами.
A* pObjA;
B* pObjB;
C* pObjC;
D* pObjD = &MyD;
// Мы начинаем с объявления соответствующих указателей.
pObjC = (C*)&MyD;
pObjA = (A*)pObjC;
// Произведена настройка указателей на требуемые фрагменты.
pObjA->x0 = 999;
// А это уже элементарно!
Очевидно, что можно обойтись без поэтапных преобразований и воспользоваться свойством коммутативности операции явного преобразования типа:
((A*)(C*)pObjD)->x0 = 5;
((A*)(B*)pObjD)->x0 = 55;
// Разным фрагментам - разные значения.
Аналогичным образом обстоят дела с функциями-членами базовых классов. Этот раздел мы завершаем небольшой программой, демонстрирующей методы доступа к членам базовых фрагментов объекта производного класса.
#include <iostream.h>
class A
{
public:
int x0;
int Fun1(int key);
};
int A::Fun1(int key)
{
cout << " Fun1( " << key << " ) from A " << endl;
cout << " x0 == " << x0 << "..." << endl;
return 0;
}
class B: public A
{
public:
int x0;
int Fun1(int key);
int Fun2(int key);
};
int B::Fun1(int key)
{
cout << " Fun1( " << key << " ) from B " << endl;
cout << " x0 == " << x0 << "..." << endl;
return 0;
}
int B::Fun2(int key)
{
Fun1(key * 5);
cout << " Fun2( " << key << " ) from B " << endl;
cout << " x0 == " << x0 << "..." << endl;
return 0;
}
class C: public A
{
public:
int x0;
int Fun2(int key);
};
int C::Fun2(int key)
{
A::x0 = 25;
Fun1(key * 5);
cout << " Fun2( " << key << " ) from C " << endl;
cout << " x0 == " << x0 << "..." << endl;
return 0;
}
class D: public B, public C
{
public:
int x0;
int Fun1(int key);
};
int D::Fun1(int key)
{
cout << " Fun1( " << key << " ) from D " << endl;
cout << " x0 == " << x0 << "..." << endl;
return 0;
}
void main ()
{
D MyD;
ObjD.x0 = 111;
A* pObjA;
B* pObjB;
C* pObjC;
D* pObjD = &MyD;
MyD.B::x0 = 100;
MyD.C::x0 = 333;
MyD.Fun1(1);
pObjD->B::Fun1(1);
pObjD->C::Fun2(1);
pObjA = (A*) (B*) pObjD;
((A*) ((C*) pObjD))->Fun1(111);
((A*) ((B*) pObjD))->Fun1(111);
pObjA->Fun1(111);
pObjC = (C*)&MyD;
pObjA = (A*)pObjC;
((A*)(B*)pObjD)->x0 = 1;
((A*)(B*)pObjD)->Fun1(777);
((A*)(C*)pObjD)->x0 = 2;
((A*)(C*)pObjD)->Fun1(999);
}