Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
posobie1.doc
Скачиваний:
13
Добавлен:
01.05.2019
Размер:
457.22 Кб
Скачать

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

При проектировании иерархических систем классов программисты обычно стремятся выделить некоторые обобщающие свойства некоторой группы описываемых сущностей в базовый класс (в предыдущих главах такими классами были Subject, Figure, Shape). Зачастую созданные по такому принципу классы носят абстрактный смысл, не описывая какой-то реально существующий объект, а лишь являясь некоторой основой, на которой строятся действительно необходимые классы. Это приводит к некоторому несоответствию спроектированной системы классов предметной области: оказывается, что программист может определить в программе объекты, реально к предметной области не имеющие никакого отношения. Для того, чтобы предотвратить возможность использования объектов таких классов в программе, их объявляют абстрактными. Абстрактным называется класс, в котором определена хотя бы одна чистая виртуальная функция. Чистая виртуальная функция определяется следующим образом:

virtual тип имя_функции( список_формальных_параметров )=0;

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

Рассмотрим пример.

//Листинг 32. Использование абстрактных классов

#include <stdio.h>

#include <iostream.h>

#include <string.h>

#include <conio.h>

class File //абстрактный класс

{ protected:

char **str; //адрес буфера для временного хранения информации из файла

char Name[30]; //имя файла

int n; //количество строк в буфере

virtual int ReadFile()=0; //чистая виртуальная функция чтения информации из файла в буфер

public:

File(char*,int);

~File();

void display(); //метод, отображающий содержимое буфера на экране

};

File::File(char * FileName,int k) //конструктор

{ strcpy(Name,FileName);

n=k;

str=new char*[n]; //выделяем память под буфер

for(int i=0;i<n;i++)

str[i]=new char[80];

}

File::~File() //деструктор

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

delete []str[i]; //освобождаем память

delete [] str;

}

void File::display()

{ clrscr();

int k=ReadFile(); //считываем информацию из файла в буфер

for(int i=0;i<k;i++)

cout<<str[i]; // построчно выводим содержимое буфера на экран

getch();

}

struct info //тип информации, хранящейся в файле

{char name[20];

char numb[10];

float value;

};

class InfoFile:public File //класс «файл с базой данных»

{ int ReadFile(); //переопределяем функцию чтения информации из файла

public:

InfoFile(char* st,int k):File(st,k) {} //конструктор вызывает конструктор базового класса

void WriteFile(int ); //метод записи информации в файл

};

int InfoFile::ReadFile()

{ FILE *fp;

int i=0;

info x;

if((fp=fopen(Name,"r"))!=NULL) //открываем файл

{ while(!feof(fp)) //пока не конец файла

{ fread(&x,sizeof(info),1,fp); //считываем очередную запись из файла

sprintf(str[i],”Запись N %d %s %s %f\n",i+1,x.name,x.numb,x.value);

//заносим очередную запись в буфер файла

i++;

}

fclose(fp);

return i-1; //функция возвращает количество считанных из файла записей

}

return 0;}

void InfoFile::WriteFile(int k)

{ info x;

FILE *fp;

if((fp=fopen(Name,"w+"))!=NULL)

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

{cout<<”Введите "<<i+1<<" -ю запись";

cin>>x.name>>x.numb>>x.value; //вводим с клавиатуры очередную запись

fwrite(&x,sizeof(info),1,fp); //записываем ее в файл

}

fclose(fp);

}}

class HelpFile:public File //класс «файл с помощью»

{ int ReadFile(); //переопределяем функцию чтения информации из файла

public:

HelpFile():File("help.dat",25){} //имя файла и его размер фиксированы

};

int HelpFile::ReadFile()

{ int i=0;

FILE *fp;

if((fp=fopen(Name,"r"))!=NULL)

{ while(!feof(fp))

{fgets(str[i],80,fp); //считываем содержимое файла в буфер

i++;

}

fclose(fp);

return i-1;

}

return 0;}

main()

{ HelpFile hp; //объект класса «файл с помощью»

hp.display(); //выводим содержимое файла на экран

InfoFile If("info",40); //создаем объект класса «файл с информацией»

If.WriteFile(3); //записываем в файл 3 записи

If.display(); //выводим содержимое файла на экран

}

Программа, приведенная в листинге 32, работает с файлами двух типов – информационным файлом (класс InfoFile), предназначенным для хранения простейшей базы данных – нескольких структур типа info, а также файлом помощи (класс HelpFile), который хранит текстовую информацию, предположительно – справку о самой программе. Общая сущность двух этих типов файлов, выражающаяся в имени файла Name, буфере str для временного хранения информации из файла, размере используемого файлом буфера n, а также метода display вывода считанной из файла информации на экран, выделена в родительский класс (класс File). Схема иерархии классов программы приведена на рис.14.

Метод display класса File считывает информацию из файла методом ReadFile в буфер и построчно выводит ее на экран. Однако, метод ReadFile в самом классе File не может быть полноценно определен, поскольку этот класс в терминах предметной области является абстрактной основой для двух других классов (InfoFile и HelpFile) и для него не известен, например, тип хранящейся в файле информации. Метод ReadFile определен в классах InfoFile и HelpFile , причем в первом данный метод считывает из файла информацию в виде экземпляров структуры info, преобразует ее в текстовую форму и записывает в буфер, а во втором - информация непосредственно считывается в виде текстовых строк, которые записываются в буфер str. Таким образом, метод ReadFile не надо определять в классе File по логике представления предметной области, однако обязательно необходимо определить по правилам синтаксиса языка С++ (так как метод display использует этот метод). При этом необходимо обеспечить полиморфное поведение метода ReadFile для того, чтобы при вызове метода display объектом класса InfoFile в теле метода display была вызвана реализация метода ReadFile для соответствующего класса (аналогичная ситуация рассматривалась в листинге 31). Поэтому метод ReadFile объявлен в классе File как чистая виртуальная функция:

virtual int ReadFile()=0;

Таким образом, класс File является абстрактным классом, для которого запрещено создание объектов, то есть ошибкой будет такое объявление:

File MyFile(“file.txt”,50);

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

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