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

2. Конструкторы порожденного класса

Если объявляется объект порожденного класса, то за инициализацию базовой части объектов должен отвечать конструктор порожденного класса. Поэтому конструктор класса B имеет специальную форму (на примере наших классов A и B):

список инициализации

B:: B(int a, int b, int c) : A(a, b, c)

{ d3 = d4 = b;}

В список инициализации может быть включена инициализация член-данных порожденного класса.

Например,

B:: B(int a, int b, int c): A(a, b, c), d3(b), d4(b) { } (1)

Заметим, что для нашего примера инициализация может выглядеть и другими способами:

B:: B(int a, int b, int c, int d, int r): A(a, b, c), d3(d), d4(r) { } (2)

Пример использования конструкторов:

void main( )

{A x, y(3, 6, 7); // конструкторы без аргументов и с аргументами класса A

B z(1, 2, 3, 4, 5); // конструктор (2): 1, 2, 3 инициализируют базовую часть объекта z (d1, d2, d3),

// а 4, 5 – порожденную (d3, d4)

}

Если в базовом классе задан конструктор по умолчанию, то в списке инициализации конструктора порожденного класса он может отсутствовать. То есть конструктор класса B может иметь вид:

B:: B(int a, int b): A( ) {d3 = a; d4 = b;}

или

B:: B(int a, int b) {d3 = a; d4 = b;}

Однако и в первом и во втором случае конструктор по умолчанию класса A работать будет. При инициализации объектов порожденного класса сначала работают конструкторы базового, а затем порожденных классов и так по всей иерархии порождения. Этот факт позволяет при инициализации член-данных порожденного класса использовать уже проинициализированные член-данные базового класса. То есть конструктор класса B может быть задан таким необычным образом:

B:: B(int a, int b, int c): A(a, b, b) // A:: d1 = a; A:: d2 = b; A:: d3 = b

{d3 = d2 * A:: d3; d4 = c;}

Деструкторы, наоборот, сначала разрушают член-данные порожденного класса, затем базового.

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

Пусть заданы объекты порожденного класса и базового

A x ; B y(1, 2, 3, 4, 5); // конструктор (2)

Что будет верным:

x = y;

или

y = x; ?

Ответ: x = y;

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

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

  2. ссылка или указатель порожденного класса преобразуются в ссылку или указатель на базовый класс.

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

Итак,

void main( )

{A x, *pA; // Для x работает конструктор по умолчанию

B y(1, 2, 3, 4, 5), *pB; // Для y работает конструктор (2)

x = y; // Верно: x.d1 = y.d1 = 1; x.d2 = y.d2 = 2; x.d3 = y.A:: d3 = 3;

y = x; // Неверно: что записать в y.d3, y.d4 из x?

// как из маленького пальто нельзя сшить большое, а наоборот – можно (см. рисунок)

pA = &y; // верно: преобразование B* –> A* определено по умолчанию

pA –> Print( ); //функция A::Print() выведет базовую часть объекта y

pB = &x; // неверно, преобразование A* –> B* неопределенно по умолчанию

pB = (A *)pA; // операция явного преобразования A* –> B*

}

Замечание. Если в классах A и B определяются конструкторы копирования

A(A &) и B(B &),

то последний имеет вид

B:: B(B &b1): A(b1) {...},

в котором в списке инициализации базовая часть объекта b1 передается конструктору копироваия класса A. При этом используется стандартное преобразование объектов порожденного класса к базовому.

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