Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ООП / ООП / oop-ekz-tasks / Семинар 5

.doc
Скачиваний:
13
Добавлен:
18.02.2017
Размер:
124.93 Кб
Скачать

Семинар 5 «Множественное наследование и RTTI»

Имеются болты (Screw), гайки (Nut) и шайбы (Washer) по отдельности.

Болт и гайка имеют метрическую или дюймовую резьбу (Thread), шайба -

метрический или дюймовый диаметр. В метрической системе диаметр задается

целым числом (мм), в дюймовой - дробью из трех цифр (например, 1+1/4 дюйма).

Из болта, гайки и произвольного (Z+) числа шайб можно собрать

соединение (Connection).

Все болты, гайки, шайбы и соединения являются объектами, хранящимися в

динамической памяти. Тип указателя для каждого объекта - указатель на общий

базовый класс Thread.

Класс Connection является наследником Screw, Nut и Washer.

Нарисовать иерархию наследования классов. В каких местах используется

виртуальное наследование?

При вызове любого конструктора и деструктора на экран выводить сообщение

(например, для класса Nut: "+Nut" для конcтруктора и "-Nut" для деструктора). Объяснить порядок их вызова.

Доп. задание: вывести кол-во гаек, болтов и шайб, разбив их на группы по диаметру.

Код

#include <vcl.h>

#pragma hdrstop

//---------------------------------------------------------------------------

#pragma argsused

//---------------------------------------------------------------------------

#include <Classes.hpp>

#include <iostream.h>

#include <conio.h>

#include <typeinfo>

//---------------------------------------------------------------------------

class Thead{

public:int l;

Thead(int x){cout<<"+Thead"<<endl;};

Thead(int x,int y,int t){cout<<"+Thead"<<endl;};

virtual ~Thead(){cout<<"-Thead"<<endl;};};

//---------------------------------------------------------------------------

class Screw: public virtual Thead{

public:

Screw(int x):Thead(x){cout<<"+Screw"<<endl;};

Screw(int x,int y,int t):Thead(x,y,t){cout<<"+Screw"<<endl;};

~Screw(){cout<<"-Screw"<<endl;};};

//----------------------------------------------------------------------------

class Nut: public virtual Thead{

public:

Nut(int x):Thead(x){cout<<"+Nut"<<endl;};

Nut(int x,int y,int t):Thead(x,y,t){cout<<"+Nut"<<endl;};

~Nut(){cout<<"-Nut"<<endl;};};

//----------------------------------------------------------------------------

class Washer: public virtual Thead{

public:

Washer(int x):Thead(x){cout<<"+Washer"<<endl;};

Washer(int x,int y,int t):Thead(x,y,t){cout<<"+Washer"<<endl;};

~Washer(){cout<<"-Washer"<<endl;};};

//---------------------------------------------------------------------------

class Connection: public Screw, public Nut, public Washer{

public:

Connection(int x,int o):Screw(x),Nut(x),Washer(x),Thead(x){cout<<"+Connection"<<endl;

l=o;};

Connection(int x,int y,int t,int o):Screw(x,y,t),Nut(x,y,t),Washer(x,y,t),Thead(x,y,t){

cout<<"+Connection"<<endl;l=o;};

~Connection(){cout<<"-Connection"<<endl;};};

//----------------------------------------------------------------------------

void main()

{int Con_count=0;

int Scr_count=0;

int Nut_count=0;

int Wsh_count=0;

Thead* t[100];

int tc=0;

t[tc++]=new Connection(5,6);

t[tc++] = new Connection(6,2);

t[tc++] = new Nut(5);

t[tc++] = new Nut(1,1,4);

t[tc++] = new Washer(0,1,2);

for (int i=0; i < tc; i++) {

if(typeid(*t[i])==typeid(Connection)){

Con_count++;

Scr_count++;

Nut_count++;

Wsh_count=Wsh_count+t[i]->l;}

if(typeid(*t[i])==typeid(Screw))Scr_count++;

if(typeid(*t[i])==typeid(Nut))Nut_count++;

if(typeid(*t[i])==typeid(Washer))Wsh_count++;}

cout<<"It has been made "<<Con_count<<" connections."<<endl;

cout<<"It has been made "<<Scr_count<<" screws."<<endl;

cout<<"It has been made "<<Nut_count<<" nut."<<endl;

cout<<"It has been made "<<Wsh_count<<" wasahers."<<endl;

for(int i=0;i<tc;i++)delete t[--tc];

getch();}

Скриншот

Рис.1. Граф «Иерархия наследования классов»

Порядок вызова деструкторов

Cначала конструируется базовая часть класса, затем производная, а при разрушении наоборот — сначала вызывается деструктор производного класса, который по окончании своей работы вызывает по цепочке деструктор базового.

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

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

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

В каких местах используется виртуальное наследование?

В данной программе виртуальный базовый класс – Thead. Части объекта Screw, Nut, Washer должны совместно использовать единственную копию Thead, в противном случае неоправданно сложно становиться избегать хранения нескольких копий объекта.

В графе на рисунке 1 все виртуальные базовые классы с одним именем будут представлены одним единственным объектом этого класса. С другой стороны, каждый невиртуальный базовый класс (Screw, Nut, Washer) будет иметь отдельный подобъект, представляющей его.

5

Соседние файлы в папке oop-ekz-tasks