Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP_otvety_k_ekzamenu.doc
Скачиваний:
55
Добавлен:
13.04.2015
Размер:
786.94 Кб
Скачать

37. Множественное наследование классов с повторяющимся базовым. Синтаксис, структура в памяти, особенности применения и реализации.

Прямое наследование повторяющихся базовых классов языком не допускается;

class A {};

class B : public A, public A {};

error C2500: 'B' : 'A' is already a direct base clas

Однако нечто подобное возможно косвенным образом, если производный класс наследуется от двух и более промежуточных, у которых имеется общий базовый класс:

class Base

{

int m_x;

public:

virtual void f () { … }

int getX () const { return m_x; }

};

class Middle1

: public Base

{

int m_y;

public:

virtual void g () { … }

};

class Middle2

: public Base

{

int m_z;

public:

virtual void h () { … }

};

class Derived

: public Middle1, public Middle2

{

int a;

public:

void f () override { … }

void g () override { … }

void h () override { … }

};

Таким образом, в объекте Derived содержимое базового класса Base представлено дважды - по каждой из веток иерархии. Внутреннее представление объекта выглядит следующим образом:

Ниже представлено соотношение размеров классов, участвующих в данной иерархии:

Размер объекта Derived определяется суммарным размером двух базовых классов + размер собственного поля. Т.е., в этот размер дважды входит размер содержимого класса Base. В свою очередь размер базовых классов определяется наличием двух информационных полей, одно из которых унаследовано из Base, + указателя vptr. При переопределении метода f в классе Derived, замещаются реализации по обеим версиям класса Base, в связи с чем в таблице виртуальных функций по ветке Middle1 используется непосредственный указатель на метод, а по ветке MIddle2 - корректирующий на 12 байт влево объект-thunk.

Интересно, что при такой структуре создается неоднозначность преобразования вверх по иерархии при попытке перейти на повторяющийся базовый класс:

Derived d;

Base * b = & d;

error C2594: 'initializing' : ambiguous conversions from 'Derived *' to 'Base *'

Неоднозначность обуславливается тем, что содержимое Base представлено в Derived дважды, и не понятно какой именно из этих экземпляров следует взять. Преобразование следует выполнить четко уточнив намерение через промежуточный базовый класс:

Derived d;

Middle1 * m1 = & d;

Base * b = m1;

либо:

Derived d;

Middle1 * m2 = & d;

Base * b = m2;

Разумеется, получаемый в итоге адрес будет различаться на соответствующее размеру класса Middle1 смещение. Из такой неоднозначности вытекает строжайший запрет на преобразование вниз по иерархии от класса Base к классу Derived напрямую, что чревато практически 100% ошибкой.

Прямое обращение к члену повторяющегося базового класса также создает неоднозначность:

Derived d;

d.getX();

Для разрешения необходимо указывать квалифицированные имена промежуточных базовых классов:

Derived d;

d.Middle1::getX();

Либо следует использовать директивы using:

class Derived

: public Middle1, public Middle2

{

...

public:

...

using Middle1::getX;

};

Порядок вызова конструкторов в данной иерархии соответствует ожидаемому, конструктор класса Base будет вызван дважды - каждый раз на разных частях результирующего объекта:

Base::Base

Middle1::Middle1

Base::Base (смещенный this на начало Middle2)

Middle2::Middle2

Derived::Derived

Деструкторы продемонстрируют поведение с точностью до наоборот:

Derived::~Derived

Middle2::~Middle2

Base::~Base (смещенный this на начало Middle2)

Middle1::~Middle1

Base::~Base

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]