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

Если базовый класс (в приведенном выше примере это класс А) является

виртуальным, то будет построен единственный объект этого класса.

#include <iostream.h>

using namespace std;

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

{ int aa;

public:

A() {cout<<"Конструктор1 класса A"<<endl;}

A(int AA) : aa(AA) {cout<<"Конструктор2 класса A"<<endl;}

~A() {cout<<"Деструктор класса A"<<endl;}

};

class B : virtual public A // производный класс (первый базовый для D)

{ char bb;

public:

B() {cout<<"Конструктор1 класса B"<<endl;}

B(int AA,char BB): A(AA), bb(BB)

{cout<<"Конструктор2 класса B"<<endl;}

~B() {cout<<"Деструктор класса B"<<endl;}

};

class C : virtual public A // производный класс (второй базовый для D)

{ float cc;

public:

C() {cout<<"Конструктор1 класса C"<<endl;}

C(int AA,float CC) : A(AA), cc(CC)

{cout<<"Конструктор2 класса C"<<endl;}

~C() {cout<<"Деструктор класса C"<<endl;}

};

class D : public C,public B // производный класс (II уровня)

{ int dd;

public:

D() {cout<<"Конструктор 1 класса D"<<endl;}

D(int AA,char BB,float CC,int DD) :

A(AA), B(AA,BB), C(AA,CC), dd(DD)

{cout<<"Конструктор 2 класса D"<<endl;}

~D() {cout<<"Деструктор класса D"<<endl;}

};

void main()

{ D d(1,'a',2.3,4);

D dd;

}

Результат работы программы:

Конструктор 2 класса A (конструкторы для объекта d)

Конструктор 2 класса C

Конструктор 2 класса B

Конструктор 2 класса D

Конструктор 1 класса A (конструкторы для объекта dd)

Конструктор 1 класса C

Конструктор 1 класса B

Конструктор 1 класса D

Деструктор класса D (деструкторы для объекта d)

Деструктор класса B

Деструктор класса C

Деструктор класса A

Деструктор класса D (деструкторы для объекта d)

Деструктор класса B

Деструктор класса C

Деструктор класса A

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

примере при создании объектов d и dd конструктор класса А вызывается из конструктора класса D первым и только один раз, затем конструкторы классов B и C, в том порядке, в котором они описаны в строке наследования классов: class D : public B, public C . В одно и то же время класс может иметь виртуальный и невиртуальный базовые классы, например:

class A{ … };

class B1: virtual public A{ … };

class B2: virtual public A{ … };

class B3: public A{ … };

class C: public B1, public B2, public B3 { … };

В этом случае класс С имеет два подобъекта класса А, один наследуемый

через классы В1 и В2 (общий для этих классов) и второй через класс В3 .

37.Абстрактные классы.

Базовый класс иерархии типа обычно содержит ряд виртуальных функ-

ций, обеспечивающих динамическую типизацию. Часто в базовом классе эти виртуальные функции фиктивны и имеют пустое тело. Эти функции существуют как некоторая абстракция, конкретная реализация им придается в производных классах. Такие функции, тело которых не определено, называются чисто виртуальными функциями. Общая форма записи чисто виртуальной функции имеет вид virtual прототип функции = 0;

Чисто виртуальная функция используется для того, чтобы отложить ре-

шение о реализации функции. То, что функция объявлена чисто виртуальной, требует, чтобы эта функция была определена во всех производных классах от класса, содержащего эту функцию. Если класс имеет хотя бы одну чисто виртуальную функцию, то он называется абстрактным. Для абстрактного класса нельзя создать объекты и он используется только как базовый класс для других

классов. Если base – абстрактный класс, то для инструкций base a;

base *p= new base;

компилятор выдаст сообщение об ошибке. В то же время вполне можно использовать инструкции вида rect b;

base *p=&b;

base &p=b;

Чисто виртуальную функцию, как и просто виртуальную функцию, необя-

зательно переопределять в производных классах. При этом если в производном классе она не переопределена, то этот класс тоже будет абстрактным, и при попытке создать объект этого класса компилятор выдаст ошибку. Таким образом, забыть переопределить чисто виртуальную функцию невозможно. Абстрактный базовый класс навязывает определенный интерфейс всем производным от него классам. Главное назначение абстрактных классов – в определении интерфейса для некоторой иерархии классов. Класс можно сделать абстрактным, даже если все его функции определены. Это можно сделать, например, чтобы быть уверенным, что объект этого класса создан не будет. Обычно для этих целей выбирается деструктор.

class base

{ компоненты-данные

public:

virtual ~base() = 0;

компоненты-функции

}

base ::~base()

{реализация деструктора}

Объект класса base создать невозможно, в то же время деструктор его оп-

ределен и будет вызван при разрушении объектов производных классов.

Для иерархии типа полезно иметь базовый абстрактный класс. Он содер-

жит общие свойства порожденных объектов и используется для объявления указателей, которые могут обращаться к объектам классов, порожденным от базового.

Proxi-классы.

Реализация скрытия данных и интерфейса некоторого класса может быть

выполнена посредством использования proxi-класса. Proxi-класс позволяет клиентам исходного класса использовать этот класс, не имея доступа к деталям его реализации. Реализация proxi-класса предполагает следующую общую структуру:

- реализация исходного класса, компоненты которого требуется скрыть;

- реализация proxi-класса для доступа к компонентам исходного класса;

- функция, в которой вызываются компоненты proxi-класса

// заголовочный файл cls.h для класса cls

class cls

{ int val;

public:

cls(int);

set(int);

int get() const;

};

// файл реализации для класса cls

#include "cls.h"

cls :: cls(int v) {val=v;}

cls :: set(int v) {val=v;}

int cls :: get() const {return val;}

// заголовочный файл prox.h для proxi-класса prox

class cls; // предварительное объявление класса cls

class prox

{ cls *pr; // для этого и требуется предварительное объявление

public:

prox(int);

set(int);

int get() const;

~prox();

};

// файл реализации для proxi-класса prox

#include "prox.h"

#include "cls.h"

prox :: prox(int vv) {pr=new cls(vv);}

prox :: set(int vv){pr->set(vv);}

int prox :: get() const {return pr->get();}

prox :: ~prox(){delete pr;}

// программа скрытия данных класса cls посредством proxi-класса prox

#include <iostream>

using namespace std;

#include "prox.h"

int main()

{ prox obj(1);

cout<<” Значение val класса cls = ”<<obj.get()<<endl;

obj.set(2);

cout<<” Значение val класса cls = ”<<obj.get()<<endl;

}

В результате выполнения программы получим:

Значение val класса cls = 1

Значение val класса cls = 2

В main()-функцию включается только файл реализации prox.cpp, при этом

отсутствует указание на существование класса cls. Следовательно, private-данные класса cls скрыты от клиента.

Наши рекомендации