Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lektsii_OOP.doc
Скачиваний:
4
Добавлен:
01.03.2025
Размер:
4 Mб
Скачать

4. Множественное наследование. Виртуальный базовый класс

4.1. Прямые базовые классы

Множественное наследование имеет место, если список порождения содержит более одного базового класса.

Например,

class C: public A, public B

{ ....};

То есть схематично это выглядит так:

Функции, определенные в классах A и B в части public, доступны объекту класса C независимо от того, в каком классе они определены.

С x(...); // действует конструктор класса A, потом B, потом C

x.Fa( );

x.Fb( );

Операция :: здесь не требуется, так как функции имеют разные имена. А если в классах A и B имелась бы функция с одинаковым именем, например, F( ), то обращение к ней с объектом класса C должно содержать операцию ::

x.A:: F( ); или x.B:: F( );

Конструктор класса C отвечает за инициализацию член-данных классов A и B, то есть должен иметь, например, такой вид:

C:: C(int x, int y, int z): A(x), B(y) {c = z;}

Рассмотренный пример – это пример множественного наследования с прямыми базовыми классами. Если же базовые классы A и B в свою очередь порождены от других базовых, то эти последние для класса C будут не прямыми базовыми (или косвенными). Приведем пример такого наследования.

4.2. Виртуальный базовый класс

В случае косвенных базовых классов может возникнуть ошибочная ситуация. Рассмотрим, в чем она заключается. Пусть, например, имеется такая иерархия порождения

Рассмотрим пристальнее, что означает такое наследование:

Эта схема показывает, что объект класса D будет содержать 2 копии член-данного int a класса A, что может привести к двусмысленности. Например, если в классе D есть функция

int D:: Geta() {return a;}

и член-данное a находится в части protected класса A, то компилятор все равно выдаст сообщение об ошибке – «двусмысленность»: так как непонятно член-данное a надо взять из части B:: A или C:: A. Избежать двусмысленности можно, указав явно класс B или C.

int D:: Geta() {return C:: a;}

Однако наличие в дереве наследования нескольких копий одного и того же базового класса не только вносит путаницу, но и приводит к лишним затратам памяти. Чтобы этого не происходило, базовый класс A в списке порождения надо объявить виртуальным:

class B: public virtual A {...};

class C: public virtual A {...};

class D: public B, public C {...};

В этом случае в объектах классов B и C будут сформированы указатели на член-данные базового класса A. То есть картина будет такой:

Э то представление классов соответствует такому дереву наследования:

Из рисунка видно, что в данном случае неоднозначности для член-данного a не будет, так как a – одна ячейка!

Замечание. В рассмотренных двух способах порождения класса D:

  1. не через виртуальный базовый класс A;

  2. через виртуальный базовый класс A

конструктор класса D определяется по-разному. В первом случае конструктор класса D не «заботится» об инициализации член-данных класса A, то есть определяется, например, так

D(int r, int s): B(r), C(s) {...}.

А во втором случае конструктор класса D отвечает и за инициализацию член-данных виртуального базового класса A. То есть должен быть задан, например, так

D(int r, int s, int q): A(r), B(s), C(q) {...}

В заключение рассмотрим пример наследования: порождение класса Kolobok из двух базовых классов Bar и Circ из п. 12 главы 1.

const int CF = 7;

class Bar

{ protected: int h, w, x, y;

public:

Bar(int hh, int ww, int xx, int yy)

{h = hh; w = ww; x = xx; y = yy;}

void Show(int c) { }

};

class Circ

{ int r;

protected: int x, y;

public:

Circ(int rr, int xx, int yy)

{r = rr; x = xx; y = yy;}

void Show(int c) { }

};

class Kolobok: public Circ, public Bar

{ int numb; char * name; // место и имя

public:

Kolobok(int h, int w, int x, int y, int r, int m, char * s):

Bar(h, w, x, y), Circ(r, x + w / 2, y - r)

{numb = m; name = new char [strlen(s) + 1];

strcpy(name, s);

}

~Kolobok() {delete [ ] name;}

void Show(int cb, int cc, int ck)

{Bar:: Show(cb); Circ:: Show(cc);

}

};

void main( )

{int x = 200, dx = 100, i;

Kolobok klb[3] = {Kolobok(100, 50, x, 150, 40, 2, “два”),

Kolobok(150, 50, x = x + dx, 100, 35, 1, “один”),

Kolobok(50, 50, x = x + dx, 200, 30, 3, “три”)

};

for(i = 0; i < 3; i++)

klb[i].Show(4, 14, 12);

Kolobok *pbc = new Kolobok(50, 50, 300, 400, 50, 4);

pbc –> Show(3, 12, 14);

delete pbc;

}

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