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

9.Абстрактные классы и виртуальные функции

Цель работы – освоить разработку иерархии классов с виртуальными функциями, основанной на абстрактном базовом классе (4 час.).

9.1.Задание

  1. По конспекту лекций освежите в памяти абстрактные и виртуальные функции, а также абстрактные классы.

  2. Ознакомьтесь с приведенными ниже методическими указаниями.

  3. Создайте заготовку консольного приложения.

  4. В соответствии с вашим вариантом задания разработайте иерархию классов, добавьте их в приложение. Базовый класс вашей иерархии должен содержать конструктор с параметрами и минимум одну абстрактную функцию; производные классы должны содержать реализацию абстрактной функции (функций) и конструкторы с параметрами.

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

9.2.Методические указания

В качестве иллюстрации разработаем иерархию классов (рис. 1) и поместим ее реализацию в файлы Base.h и Base.cpp (проект ACVF).

// Файл Base.h

#pragma once

class CBase

{

public:

virtual char * getName()=0;

};

class CFirst: public CBase

{

double *dFoo;

public:

virtual char * getName();

CFirst();

~CFirst();

};

class CSecond: public CFirst

{

int *iFoo;

public:

CSecond();

~CSecond();

virtual char * getName();

};

Рис. 1. Иерархия классов

Реализацию методов класса поместим в файл Base.cpp и добавим в конструкторы и деструкторы вывод сообщений об их вызове. Для вывода сообщений можно было бы использовать макросы TRACE, но в данном случае мне показалось удобнее использовать для вывода cout.

// Файл Base.cpp

#include "StdAfx.h"

#include "Base.h"

using namespace std;

CBase::~CBase()

{

cout<<"CBase::~CBase"<<endl;

}

CFirst::CFirst()

{

dFoo=new double [100];

cout<<"CFirst::CFirst"<<endl;

}

CFirst::~CFirst()

{

cout<<"CFirst::~CFirst"<<endl;

delete [] dFoo;

}

CSecond::CSecond()

{

cout<<"CSecond::CSecond"<<endl;

iFoo=new int [100];

}

CSecond::~CSecond()

{

cout<<"CSecond::~CSecond"<<endl;

delete [] iFoo;

}

char * CFirst::getName()

{return "CFirst";}

char * CSecond::getName()

{return "CSecond";}

Для проверки виртуальных функций разработаем глобальные функции, которые получают объекты классов иерархии в качестве параметров:

Void PrintClassNamе1(cBasе *pb) // параметр-указатель

{

сout<<pb->gеtNamе()<<еndl;

}

Void PrintClassNamе2(cBasе &b) // параметр-ссылка

{

сout<<b.gеtNamе()<<еndl;

}

Void PrintClassNamе3(cFirst f) // параметр-значение

{

сout<<f.gеtNamе()<<еndl;

}

Теперь поместим в главную функцию код, который позволит испытать полиморфные объекты иерархии:

int _tmain(int argс, TCHAR* argv[], TCHAR* еnvp[])

{

//...

CBase *obj;

obj=new CFirst; // вывод CFirst::CFirst

cout<<endl;

PrintClassName1(obj); // выводит CFirst

cout<<endl;

PrintClassName2(*obj); // выводит CFirst

cout<<endl;

delete obj; //Вывод CBase::~CBase!! Деструктор ~CFirst не вызван!

cout<<endl;

obj=new CSecond; // вывод CFirst::CFirst CSecond::CSecond

PrintClassName1(obj); // выводит CSecond

cout<<endl;

delete obj; //Вывод CBase::~CBase!! Деструкторы ~CSecond и ~CFirst

// не вызваны!

CFirst * fobj=new CSecond; // вывод CFirst::CFirst CSecond::CSecond

cout<<endl;

PrintClassName1(fobj); // выводит CSecond

cout<<endl;

PrintClassName2(*fobj); // выводит CSecond

cout<<endl;

PrintClassName3(*fobj); // выводит CFirst, затем CFirst::~CFirst

// и CBase::~CBase

cout<<endl;

delete fobj; /* Вывод CFirst::~CFirst, после чего программа снимается

по ошибке с указанием в качестве возможной причины на

разрушение heap */

_gеtсh();

rеturn nRеtCodе;

}

Намотайте на ус, что в операторе obj=new CFirst указатель на объект obj объявлен как имеющий тип CBase, но создается объект производного типа CFirst! Подобным образом должны действовать и вы при выполнении своего индивидуального задания.

С этим все понятно, так? Но вот первая проблема: создается объект класса CSecond с помощью оператора CFirst * fobj=new CSecond, как и положено, вызываются конструкторы CFirst и CSecond (именно в этой последовательности, в соответствии со спецификацией языка), но вот при выполнении оператора PrintClassName3(*fobj) вызываются только деструкторы ~CFirst и ~CBase, а вот деструктор ~CSecond не вызывается, хотя должен бы, уродина! То, что при этом не вызываются конструкторы при передаче объекта как параметра функции – песнь отдельная (см. методический материал л.р. «Перегрузка векторных и матричных операций»).

Как печальный результат, выполнение оператора delete fobj приводит к (повторному!) вызову деструктора класса ~CFirst и снятию программы по ошибке при выполнении оператора освобождения памяти в этом деструкторе. Если же убрать оператор PrintClassName3(*fobj), то после завершения программы обнаружатся утечки памяти.

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

CBase *obj;

obj=new CFirst;

Выход очень простой: надо объявить конструкторы классов иерархии виртуальными, причем достаточно это сделать только в базовом классе. Проверьте!

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