3. Стандартные преобразования при наследовании
Преобразования по умолчанию
Примеры
Иллюстрация
Продолжение примера
Замечание
4. Множественное наследование. Виртуальный базовый класс
Схематично это выглядит так:
Одинаковые имена
Конструктор C::
4.2. Виртуальный базовый класс
Рассмотрим подробно, что означает такое наследование
Двусмысленность(ambiguity)
Виртуальный A
В этом случае в объектах классов B и C будут сформированы указатели на член-данные базового класса A.
Это представление классов соответствует такому дереву наследования
Замечание о конструкторе D::
659.00K
Category: programmingprogramming

Стандартные преобразования при наследовании

1.

Использовали классы:
class A
{ int d1;
protected d2, d3;
public:
……..
int Get1() { return d1;}
void Print(){ cout<<d1<<’ ‘<<d2<<’ ‘<<d3; }
friend ostream& operator<< (ostream&, B&);
};
class B: public A
{ int d3, d4;
public:
….
friend ostream& operator<< (ostream&, B&);
};
ostream& operator<< (ostream& r, B& x)
{ r << x.Get1() <<’ ‘<< x.d2 <<’ ‘<< x.A::d3 <<’ ‘<< x.d3<<’ ‘<< x.d4;
return r;}

2. 3. Стандартные преобразования при наследовании

Пусть заданы объекты порожденного и базового класса:
// конструктор (2)
A x ; B y(1,2,3,4,5);
Что будет верным:
x = y;
// базовый <== порожденный?
y = x; ?
// порожденный <== базовый?
или
Ответ x = y;

3. Преобразования по умолчанию

При наследовании действуют по умолчанию такие
правила преобразования:
1. Объект порожденного класса
объекту
базового
(путем
порожденных член-данных).
преобразуется к
отбрасывания
В обратную сторону преобразование не определено
(и может быть задано только пользователем, как
правило, конструктором вида B(A&) ).

4.

2. Ссылка или указатель порожденного класса
преобразуются в ссылку или указатель на базовый
класс.
Преобразование указателя на объект базового
класса к указателю на объект порожденного может
быть выполнено явно по операции (тип).

5. Примеры

void main()
{A x, *pA;
B y(1, 2, 3, 4, 5), *pB;
/* Для объекта x работает
конструктор по умолчанию */
/* Для объекта y работает
конструктор (2) */
x = y;
/* Верно (правило 1): x.d1 = y.d1 = 1;
x.d2 = y.d2 = 2; x.d3 = y.A::d3 = 3; */
y = x;
/* Неверно:
что записать в y.B::d3, y.d4 из x? */

6. Иллюстрация

Правило
преобразования 1.
X:
d1 = 10; d2 = 20; d3 = 30;
X =Y !!
Y:
A::
d1 = 10; d2 = 20; d3 = 30;
В обратную сторону
преобразование
не определено!
??
Y=X??
d3 = 4; d4 = 5;
// как из маленького пальто (X) нельзя сшить большое (Y),
// а наоборот - можно.

7. Продолжение примера

pA = &y;
/* верно: преобразование B*->A*
определено по умолчанию (правило 2) */
pA->Print();
/* функция A::Print() выведет
базовую часть объекта y */
pB = &x;
/* неверно, преобразование A* ->B* не
определено по умолчанию */
pB = (B *)&x;
/* правило 3: операция явного преобразования A* -> B*(если определим!) */
cout<<pB->Get1();
}
// вывод x.d1

8. Замечание

Если в классах A и B определяются конструкторы
копирования A(const A &)
и B(const B &), то
последний имеет вид
B::B(const B &b1): A(b1) {...},
в
списке инициализации которого базовая часть
объекта b1 передается конструктору копирования
класса A.
При этом используется стандартное преобразование
объектов порожденного класса к базовому (правило 1).
1. Объект порожденного класса преобразуется к объекту
базового (путем отбрасывания порожденных член-данных).

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

4.1 Прямые базовые классы
Множественное наследование имеет место,
если список порождения содержит более
одного базового класса.
Например,
class C: public A, public B
{ ....};

10. Схематично это выглядит так:

A::
Fa(); int a;
B::
C::
… int c;
Fb(); int b;

11.

Функции, определенные в классах A и B в части public,
доступны объекту класса C независимо от того, в
каком классе они определены.
Например:
С x(...);
/* действует конструктор класса A,
потом B, потом C */
x.Fa();
x.Fb();
/* Операция :: здесь не требуется, так как
функции имеют разные имена. */
A::
Fa(); int a;
B::
C::
… int c;
Fb(); int b;

12. Одинаковые имена

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

13. Конструктор C::

Конструктор класса C отвечает за инициализацию
член-данных классов A и B, т.е. должен иметь,
например, такой вид:
C::C(int x, int y, int z) : A(x), B(y) { c = z;}
список инициализации
A::
Fa(); int a;
B::
C::
… int c;
Fb(); int b;

14.

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

15.

class A
{protected: int a;
public: void F();…};
class E
{protected: int e;
public: void Fe();…};
class B: public A
{protected: int b;
public: void Fb();…};
class C: public E
{protected: int c;
public: void F();…};
class D: public B, public C
{int d;
public: void Fd(); …};
A::
int a;
void F();
int b;
B:: void Fb();
E::
int e;
void Fe();
C::
int c;
void F();
int d;
D:: void Fd();

16.

B::B(int a_, int b_): A(a_), b(b_) {}
C::C(int e_, int c_): E(e_) { c = c_;}
D::D(int a_, int b_, int e_, int c_, int d_):
B(a_, b_), C(e_, c_), d(d_) {}
A::
int a;
void F();
E::
int e;
void Fe();
B::
int b;
void Fb();
C::
int c;
void F();
D::
int d;
void Fd();

17.

void main()
{B b(1, 2); C c(3, 4);
D d(1, 2, 3, 4, 5); //a=1, b=2, e=3, c=4, d=5
d.a = 8;
d.Fe(); d.Fb(); d.Fd(); d.F();
d.A::F();
c.Fe(); c.Fb(); c.Fd(); c.F();
b.Fe(); b.Fb(); b.Fd(); b.F();
}
A::
int a;
void F();
E::
int e;
void Fe();
B::
int b;
void Fb();
C::
int c;
void F();
D::
int d;
void Fd();

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

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

19. Рассмотрим подробно, что означает такое наследование

Член-данные, наследуемые из класса A::
int a;
Собственные член-данные класса B
B
D:: объект
C
Член-данные, наследуемые из класса A::
int a;
Собственные член-данные класса C
Собственные член-данные класса D
A
A
B
C
D
Эта схема показывает, что объект класса D
будет содержать 2 копии член-данного int a
класса A, что может привести к
двусмысленности.

20. Двусмысленность(ambiguity)

Например, если в классе D есть функция
int D::Geta(){ return a; }
и член-данное a находится в части protected
класса A (и типе порождения не private), то
компилятор все равно выдаст сообщение об
ошибке – «двусмысленность».
Причина - непонятно член-данное a надо взять
из части B::A или C::A?

21.

Избежать двусмысленности можно, указав явно
класс B или C:
int D::Geta() { return C::a; }
Однако наличие в дереве наследования
нескольких копий одного и того же базового
класса не только вносит путаницу, но и
приводит к лишним затратам памяти.

22. Виртуальный A

Чтобы этого не происходило, базовый класс A в
списке
порождения
надо
объявить
виртуальным:
class B: public virtual A {...};
class C: public virtual A {...};
class D: public B, public C{...};

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

То есть картина будет такой:
B
D
C
Указатели на ч/д
класса A
Собственные ч/д
класса B
Указатели на ч/д
класса A
Собственные ч/д
класса C
Собственые ч/д
класса D
ч/д класса A::
int a;
Из рисунка видно, что
в данном случае
неоднозначности для
член-данного a не
будет, т.к.
a - одна ячейка!

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

A
B
C
D

25. Замечание о конструкторе D::

В рассмотренных двух способах порождения класса D:
1. не через виртуальный базовый класс A;
2. через виртуальный базовый класс A
конструктор класса D определяется по-разному.
В первом случае конструктор класса D не «заботится» об
инициализации член-данных класса A, т.е. определяется,
например, так
D(int r, int s): B( r ), C( s ) {...}.
В
A нет.
A списке
A инициализации
A
B
C
D
B
C
D
A
A
B
C
D

26.

А во втором случае конструктор класса D отвечает и за
инициализацию член-данных виртуального базового
класса A.
Т.е. должен быть задан, например, так
D(int r, int s, int q): A( r), B( s ), C(q ){...}
A
B
C
D
English     Русский Rules