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


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

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



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

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

Например,

 

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 будут не прямыми базовыми (или косвенными). Приведем пример такого наследования.

 

 

 

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

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

 

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

Эта схема показывает, что объект класса 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;

 }

 



Поделиться:


Последнее изменение этой страницы: 2021-12-07; просмотров: 52; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.117.72.224 (0.006 с.)