Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lutsik_Yu_A_Obektno_orientir_programmir_na_yaz.pdf
Скачиваний:
63
Добавлен:
11.05.2015
Размер:
4.33 Mб
Скачать

else

 

{ update(odd,even);

// создание odd модели из even модели

pr_state(odd);

// вывод сгенерированной модели odd

dele(even);

// удаление модели even

}

 

}

 

return 0;

 

}

 

4.4. Виртуальные деструкторы

Р

 

Виртуальные деструкторы необходимы при использовании указателей на

 

И

базовый класс при выделении памяти под динамически создаваемые объекты

производных классов. Это обусловлено тем, что если объект уничтожается яв-

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

но, например, используя операцию delete, то вызывается деструктор только

рукторы производных классов становятся такжеГвиртуальными. При этом если будет выполняться явное уничтожение объекта производного класса для указа-

В случае объявления деструктора базового класса виртуальным, все дест-

теля на базовый класс, то вначале вызыв ется деструктор производного класса,

а затем вверх по иерархии до дестру т

Б

б зового класса.

деструкторов и в случае их отсу ствия на примере программы, вычисляющей

ора Рассмотрим отличия в рабокпрогр ммы при использовании виртуальных

 

 

 

т

площади некоторых фигур (круг, прямоугольник).

 

#include <iostream>

те

 

 

о

 

 

#include <iomanip>

 

 

using namespace std;

 

 

#include <string.h>

 

 

л

 

 

// базовый класс «фигура»

 

class Shape

 

 

 

б

 

 

 

 

{protected:и

 

// площадь фигуры

 

float s;

 

 

и

 

 

 

 

public:

 

 

 

Б

Shape(char *fig) : s(0)

{ cout << "конструктор класса Shape (фигура "<< fig <<')'<< endl;} virtual ~Shape()

{ cout << "деструктор класса Shape" << endl;} void virtual print()

{cout<<s<<endl;} void virtual area()=0;

};

class Circle : public Shape // производный класс «круг»

{ int r;

91

public:

Circle(char *name,int r): Shape(name)

{cout << "конструктор класса Circle "<<endl; this->r=r;

}

~Circle()

{ cout << "деструктор класса Circle " << endl;} void area();

};

 

Р

class Bar : public Shape // производный класс «прямоугольник»

{ int n,m;

 

public:

 

Bar(char *name,int n,int m): Shape(name)

 

И

{ cout << "конструктор класса Bar "<<endl;

ГУ

 

this->n=n;

 

this->m=m;

 

}

Б

 

~Bar()

 

{ cout << "деструктор класса Bar " << endl;}

 

void area();

 

};

 

 

 

 

 

 

 

 

а

void Circle::area()

 

 

 

 

 

 

 

 

 

к

{ s=r*r*3.14;

 

 

 

 

 

 

cout<<"Площадь круга = ";

е

 

 

this->print();

 

 

 

 

}

 

 

т

 

 

 

 

 

 

 

 

 

void Bar::area()

 

 

 

 

{ s=n*m;

 

 

 

о

 

 

 

 

cout<<"П ощадь прямоугольника = ";

 

 

this->print();

и

 

 

 

 

}

 

 

л

 

 

 

 

 

int main()

 

 

 

 

 

 

 

{

 

б

 

 

 

 

 

 

Shape *fg1,*fg2;

 

 

 

 

 

 

fg1=newиCircle("Круг",2);

 

 

 

 

fg2=new Bar("Прямоугольник",3,4);

 

Б

 

 

 

 

 

 

 

 

fg1->area(); fg2->area(); delete fg1; delete fg2; return 0;

}

92

Результат работы программы: конструктор класса Shape (фигура Circle) конструктор класса Circle

конструктор класса Shape (фигура Bar) конструктор класса Bar

площадь круга =12.56

площадь прямоугольника =12

 

 

 

деструктор класса Circle

 

 

 

деструктор класса Shape

 

 

 

деструктор класса Bar

 

 

 

деструктор класса Shape

 

 

 

В случае если деструктор базового класса не являлся бы виртуальным, то

при удалении объектов производных классов осуществлялся Рбы вызов только

 

У

деструктора класса соответствующего ему типа , т.е. базового класса (класса,

Г

И

для которого объявлен соответствующий указатель).

 

 

Если в классе имеются виртуальные функции,

то желательно объявлять

деструктор этого класса также виртуальным, даже если этого не требуется. Это может предотвратить возможные ошибки.

4.5. Множественное наследов ние

Б

В языке С++ имеется возможность образовывать производный класс от

нескольких базовых классов. Общая фор

 

множественного наследования име-

ет вид

 

 

ма

 

 

к

 

 

 

 

 

 

class имя_произв класса : имя_базового_кл 1,…,имя_базового_кл N

{ содержимое класса

е

 

 

 

};

 

 

 

 

 

Иерархическая с рук ура, в которой производный класс наследует от не-

скольких базовых класств, называется множественным наследованием. В этом

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

protected- и public-компонентам базовых классов.

 

и

 

 

 

 

Конструкторы базовых классов при создании объекта производного клас-

са вызываютсялв том порядке, в котором они указаны в списке при объявлении

зводного класса.

 

 

 

 

б

 

 

 

 

 

При применении множественного наследования возможно возникновение

прои Бнескольких конфликтных ситуаций. Первая − конфликт имен методов или ат-

рибутов нескольких базовых классов: class A

{public: void fun(){} };

class B

{public: void fun(){} };

93

class C : public A, public B

{ };

int main()

 

{ C *c=new C;

 

c->fun();

// error C::f' is ambiguous

return 0;

 

}

При таком вызове функции fun() компилятор не может определить, к ка- кой из двух функций классов A и B выполняется обращение. Неоднозначность можно устранить, явно указав, какому из базовых классов принадлежит вызы-

ваемая функция:

 

 

 

 

 

 

 

 

Р

c->A:: fun();

 

или

c->B::fun();

 

 

 

 

 

 

 

 

 

 

 

 

Вторая проблема возникает при многократном включении некоторого

базового класса:

 

 

 

 

 

 

 

И

#include <iostream>

 

 

 

 

 

 

 

 

 

 

 

У

 

using namespace std;

 

 

 

 

 

 

#include <string.h>

 

 

 

 

 

 

 

 

 

 

Г

 

 

class A

 

 

 

 

 

 

 

 

 

 

// базовый класс I уровня

 

 

 

{ char naz[20];

 

//

название фирмы

Б

 

 

 

public:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

A(char *NAZ) {strcmp(naz,NAZ);}

 

 

 

 

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

 

 

 

void a_prnt() {cout << naz << endl;}к

 

 

 

 

};

 

 

 

 

е

 

 

 

 

class B1 : public A

 

 

 

 

 

 

 

 

 

// производный класс (1 Базовый II уровня)

{ protected:

 

 

 

т

 

 

 

 

long tn;

 

 

 

 

 

 

int nom;

 

 

 

 

 

 

 

о

 

 

 

 

 

public:

 

 

 

 

 

 

 

B1(char *NAZ,longиTN,int NOM): A(NAZ),tn(TN),nom(NOM) {};

~B1() {cout << "деструктор класса В1" << endl;}

 

 

 

void b1

л

 

 

 

 

 

 

 

prnt()

 

 

 

 

 

 

 

 

{ A::aбprnt();

 

 

 

 

 

 

 

 

cout << " таб. N " << tn <<" подразделение = " << nom <<endl;

и

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

};

 

 

 

 

 

 

 

 

 

Б

 

 

 

// производный класс (2 Базовый II уровня)

class B2 : public A

 

 

{ protected:

 

 

 

 

 

 

 

 

 

double

zp;

 

 

 

 

 

 

 

 

public:

 

 

 

 

 

 

 

 

 

B2(char *NAZ,double ZP): A(NAZ),zp(ZP) {}; ~B2(){cout << "деструктор класса В2" << endl;}

94

void b2_prnt()

{A::a_prnt();

cout << " зар/плата = " << zp << endl;

}

};

class C : public B1, public B2 // производный класс ( III уровня) { char *fam;

public:

C(char *FAM,char *NAZ,long TN,int NOM,double ZP) : B1(NAZ,TN,NOM), B2(NAZ,ZP)

 

{

fam = new char[strlen(FAM)+1]

 

 

 

Р

 

};

strcpy(fam,FAM);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

 

 

void c_prnt()

 

 

 

 

 

 

И

 

 

 

 

 

 

У

 

 

{ B1::b1_prnt();

 

 

 

 

 

 

 

 

B2::b2_prnt();

 

 

 

 

 

 

 

 

 

 

 

 

Г

 

 

 

 

cout << " фамилия " << fam<<endl;

 

 

};

}

 

 

 

 

 

 

Б

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

а

 

 

 

 

int main()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

{ C cc("Иванов","мастра",1234,2,555.6),*pt=&cc;

 

 

//

cc.a_prnt();

ошибка

к

 

 

 

 

 

'C::a_prnt' is ambiguous

 

 

//

pt->a_prnt();

 

е

 

 

 

 

 

 

 

cc.b1_prnt();

 

 

 

 

 

 

 

 

т

 

 

 

 

 

 

 

 

pt->b1_prnt();

 

 

 

 

 

 

 

 

cc.b2_prnt();

 

 

 

 

 

 

 

 

pt->b2 prnt();

 

 

 

 

 

 

 

 

 

 

 

 

о

 

 

 

 

 

 

 

 

 

cc.c prnt();

 

 

 

 

 

 

 

 

 

 

pt->c

и

 

 

 

 

 

 

 

 

 

 

prnt();

 

 

 

 

 

 

 

 

 

 

return 0;

 

 

 

 

 

 

 

 

 

 

}

л

 

 

 

 

 

 

 

 

 

 

б

 

 

 

 

 

 

 

 

 

 

 

В приведенном примере производный класс С имеет по цепочке два оди-

наковыхибазовых класса А (A<-B1<-C и A<-B2<-C), для каждого базового клас-

са А строится свой объект (рис. 3, 4). Таким образом, вызов функции

Б cc.a_prnt();

 

 

 

 

 

 

 

 

 

 

pt->a_prnt();

 

 

 

 

 

 

 

 

 

 

некорректен, так как неизвестно, какую из двух функций (какого из двух клас- сов А) требуется вызвать.

95

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