Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Задание С++ 07_10_08_.doc
Скачиваний:
25
Добавлен:
11.03.2016
Размер:
1.15 Mб
Скачать

Задание 5.3 Использование указателей на объекты классов и объекты классов в качестве элементов классов

Разработать классы, описывающие учетную информацию некоторой предметной области из задания 5.0. В качестве элементов классов должны использоваться указатели на объекты классов задания 5.2 и объекты классов задания 5.1.

Пример. Предметная область библиотека.classBook(Книга)

{ Name autor; // автор

Title title; // название

long number; // инвентарный номер

Price price; // цена

public:

Book();

Book (Name, Title, long, Price);

//…

// другие функции

};

classReader (Читатель)

{ Namename; //имя читателя

longnumber; // номер читательского билета

Address address; //адрес читателя

Telephone telephone; // телефон читателя

public:

Reader();

Reader (Name, Address, Telephone);

intvalidate(); проверяет корректность данных

intfinput(char*filename); читает запись из файла

intfoutput(char*filename);сохраняет запись в файл

//…

// другие функции

};

class BookLibrary {

Book* b;

Reader* r;

Name bname; //имя библиотекаря

DateTime data; // дата выдачи книги читателю

public:

BookLibrary ();

BookLibrary (Book* ,Reader*, Name, DateTime);

intvalidate(); проверяет корректность данных

intfinput(char*filename); читает запись из файла

intfoutput(char*filename);сохраняет запись в файл

//…

// другие функции

};

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

Задание 5.4 Система управления данными.

Разработать программу, аналогичную программе задания 3, которая позволяет выполнять набор определенных операций с объектами классов задания 5.2, 5.3. Классы, описывающие учетную информацию некоторой предметной области, должны содержать адреса предварительно определенных объектов классов задания 5.2.

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

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

Полный пример разработки программы для VS 2005 приведен в приложении 3

Пример пошаговой разработки программы

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

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

class Book (Книга)

{

MyString autor;

MyString title;

int number;

int price;

public:

Book();

//…

// другие функции

};

class Reader

{

MyString name;

MyString address;

int number;

int telephon;

public:

Reader();

//…

// другие функции

};

class Library

{ Book* b;

Reader* r;

MyString bibl;

MyDate d;

public:

Library();

//…

// другие функции

};

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

В качестве элементов неоднородного контейнера берется указатель на базовый класс для всех используемых типов. Базовый класс является абстрактным. Нельзя создать объект базового класса. Он содержит чистые виртуальные функции, которые определяют свойства, общие для всех объектов. К ним относятся свойства копирования и удаления объектов, их сравнения, а также возможность получить реальное имя типа данных.

Файл Obj.h

#pragma once

class MyString;

class Obj{

public:

virtual ~Obj(){}

virtual MyString* ToMyString()=0;

virtual Obj* copy()=0;

virtual void dispose ()=0;

virtual int equal(const Obj&)=0;

virtual int cmp(const Obj&)=0;

};

Более частными свойствами являются свойства объектов, связанных с возможностью их ввода-вывода на различные устройства. Для описания таких свойств введем абстрактный класс InOut, наследованный от Obj. С каждым устройством ввода-вывода можно связать отдельную виртуальную функцию, которую необходимо переопределять в каждом производном классе. При большом количестве устройств это приведет к росту необходимого программного кода, большая часть которого будет повторяться. Решение этой проблемы может заключаться в выделении одного формата или устройства в качестве базового ( фундаментального) и переопределении виртуальных функций только для него. Остальные устройства должны иметь средства для преобразования общего формата представления данных в свой собственный. В качестве общего формата представления данных используем массив строк языка Си (тип char*), а в качестве дополнительных устройств ввода-вывода TextBox и ListView.

Файл io.h

#pragma once

#include "Obj.h"

class InOut :public Obj{

public:

virtual int input(File fp)=0;

virtual int input()=0;

virtual int output(File f)=0;

virtual int output()=0;

virtual int input (char* m[], int first=0)=0 ;

virtual int output (char* m[], int first=0)=0;

int input (cli::array<TextBox^>^ m, int count, int first=0);

int output (cli::array<TextBox^>^ m, int count, int first=0);

int output(ListView^ lis, int count);

};

Вспомогательный класс Buff используется для создания и удаления временного массива строк, в который организуется ввод-вывод данных из объектов производных классов с помощью переопределенных виртуальных функций. Преобразование из массива строк в TextBox и ListView осуществляется при помощи соответствующего статического метода класса MyConvert

Файл io.cpp

#include "io.h"

class Buff{

char** ms;

int count;

public:

char**get(){return ms;}

Buff(int _count,int max)

{ ms=new char*[count=_count];

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

ms[i]=new char[max];

}

~Buff()

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

delete [] ms[i];

delete []ms;

}

};

int InOut::input (cli::array<TextBox^>^ m, int count, int first)

{Buff b(count,MAX);

MyConvert::copyto(b.get(),m,count,first,0);

return input(b.get());

}

int InOut::output (cli::array<TextBox^>^ m, int count, int first)

{Buff b(count,MAX);

int state= output(b.get());

MyConvert::copyto(m,b.get(),count,0,first);

return state;

}

int InOut::output(ListView^list, int count)

{Buff b(count,MAX);

int state=output(b.get());

cli::array<String ^ >^m=gcnew cli::array< System::String^ >(count);

//String* m[]=new String*[count];

MyConvert::copyto(m,b.get(),count);

list->Items->Add(gcnew ListViewItem(m));

return state;

}

Классы MyString и MyDate наследуем от класса InOut. В классе должны быть переопределены все виртуальные функции абстрактных базовых классов InOut и Obj, в том числе функции int equal(const Obj& t) и int cmp(const Obj& t) , сравнивающие объекты типа Obj*. В их реализации необходимо явное приведение указателя на базовый класс к указателю на соответствующий производный и вызов метода, определенного для сравнения объектов производного класса.

Файл MyString.h

#pragma once

#include "io.h"

class MyString: public InOut

{char* s;

int len;

public:

~MyString(void);

void dispose ();

MyString* copy();

String^ ToString();

MyString* ToMyString();

int equal(const MyString& t);

int cmp(const MyString& t);

int equal(const Obj& t);

int cmp(const Obj& t);

int input (char* m[], int first=0) ;

int output(char* m[], int first=0);

int input(File fp) ;

int input() ;

int output(File f);

int output();

int input(TextBox^ t);

};

Файл MyString.cpp

#include "MyString.h"

MyString* MyString::copy()

{return new MyString(*this);}

MyString* MyString::ToMyString(){return new MyString(*this);}

int MyString::equal(const MyString& ss)

{return strcmp(s,ss.s)==0;}

int MyString::cmp(const MyString& ss)

{return strcmp(s,ss.s);}

int MyString::equal(const Obj& t)

{return equal((const MyString&)t);}

int MyString:: cmp(const Obj& t)

{return cmp((const MyString&)t);}

int MyString::input (char* m[], int first)

{char*ss=new char[(len=strlen(m[first]))+1];

strcpy(ss,m[first]);

delete [] s;

s=ss;

return len;}

int MyString::output(char* m[], int first)

{delete [] m[first];

m[first]=new char[len+1];

strcpy(m[first],s);

return len;}

Файл MyDate.h

#pragma once

#include "io.h"

#include "MyString.h"

class MyDate:public InOut

{ int day,month,year;

Файл MyDate.cpp

#include "MyDate.h"

#include "MyConvert.h"

int MyDate::equal(const MyDate& d)

{return day==d.day && month==d.month && year==d.year;}

int MyDate::cmp(const MyDate& d)

{if(year!=d.year)return year-d.year;

else if(month!=d.month)return month-d.month;

else return day-d.day;

}

int MyDate::equal(const Obj& t)

{return equal((const MyDate&)t);}

int MyDate::cmp(const Obj& t)

{return cmp((const MyDate&)t);}

int MyDate::input (char* m[], int first)

{MyConvert::copyto(&day,m[first++]);

MyConvert::copyto(&month,m[first++]);

MyConvert::copyto(&year,m[first++]);

return 3;}

int MyDate::output(char* m[], int first)

{MyConvert::copyto(m[first++],day);

MyConvert::copyto(m[first++],month);

MyConvert::copyto(m[first++],year);

return 3;}

Структуры отличаются от других данных наличием соответствующего типа полей с определенными именами. Это свойство можно отразить с помощью введения общего базового класса, интерфейс которого включает виртуальные функции такие, как сравнение по полю с заданным именем, определение количества полей, получение всех имен полей. Они должны быть переопределены для конкретных структур. Использование данных виртуальных функций позволяет в общем виде для всех структур задать функции сравнения с управляющим параметром и функцию, определяющую нахождение в диапазоне значений

Файл Record.h

class Record : public InOut

{public:

virtual int CmpField(const Obj& obj,char*s)=0;

virtual int CountFields()=0;

virtual char** NamesFields()=0;

static int CmpParamSort( Obj* a, Obj* b,ParamSort* p);

int InRangeValue(InRange* param);

};

struct ParamSort {

char* FieldName[3];

int DirectCond[3];

ParamSort()

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

{FieldName[i]=0;DirectCond[i]=0;}

}

};

struct InRange {

Obj* Min,*Max;

char*s;

InRange(Obj* m1, Obj* m2,char* s0): Min(m1),Max(m2), s(s0){}

};

Файл Record.cpp

#include "Record.h"

int Record::InRangeValue(InRange* param)

{return CmpField((Obj&)(*(param->Min)),param->s)>=0 && CmpField((Obj&)(*(param->Max)),param->s)<=0 ;}

int Record::CmpParamSort( Obj* aa, Obj* bb,ParamSort* p)

{ Record* a= (Record*)aa;

Record* b= (Record*)bb;

if(a->CmpField(*b,p->FieldName[0])!=0)

if(p->DirectCond[0]==1) return a->CmpField(*b,p->FieldName[0])>0;

else return a->CmpField(*b,p->FieldName[0])<0;

else if(a->CmpField(*b,p->FieldName[1])!=0)

if(p->DirectCond[1]==1) return a->CmpField(*b,p->FieldName[1])>0;

else return a->CmpField(*b,p->FieldName[1])<0;

else if(a->CmpField(*b,p->FieldName[2])!=0)

if(p->DirectCond[2]==1) return a->CmpField(*b,p->FieldName[2])>0;

else return a->CmpField(*b,p->FieldName[2])<0;

}

Файл Book.h

class Book:public Record

{

MyString autor;

MyString title;

int number;

int price;

public:

MyString* ToMyString(){return new MyString("Book");}

Book* copy();

void dispose ();

int equal(const Book&); // функция, проверяющая равенство значений полей записей

//Используется при поиске

int cmp(const Book&); // функция, сравнивающая записи Используется при сортировки

friend int CmpField(Book* a,Book* b,char*s);

int CmpField(const Obj& obj,char*s);

int CountFields() {return 4;}

int equal(const Obj&);

int cmp(const Obj&);

int finput(File f);

int input(File fp) ;

int input() ;

int output(File f);

int output();

int input (char* m[], int first=0) ;

int output(char* m[], int first=0);

};

Файл Book.cpp

int Book::equal( const Book& r)

{ return

((r.autor.length()!=0)? autor.equal(r.autor) :1)&&

((r.title.length()!=0)? title.equal(r.title) :1)&&

((r.price !=0)? r.price == price :1) &&

((r.number !=0)? r.number== number :1)

;

}

int Book::cmp(const Book& t) // функция, сравнивающая записи

{int cond;

MyString s1(t.autor);

MyString s2(t.autor);

if(s1.length()!=0&& (cond=autor.cmp(t.autor)))return cond;

else if(s2.length()!=0&& (cond=title.cmp(t.title)))return cond;

else return number-t.number;

}

int Book::equal(const Obj& t)

{return equal((const Book&) t);}

int Book::cmp(const Obj& t)

{return cmp((const Book&) t);}

int CmpField(Book* a,Book* b,char*s)

{if (!strcmp(s,"autor")) return a->autor.cmp(b->autor);

else if(!strcmp(s,"title")) return a->title.cmp(b->title);

else if (!strcmp(s,"price")) return a->price-b->price;

else if (!strcmp(s,"number")) return a->number-b->number;

}

int Book::CmpField(const Obj& obj,char*s)

{return ::CmpField(this,(Book*)&obj,s);}

int Book::input (char* m[], int first)

{int state=1;

state*=autor.input(m,first++) ;

state*=title.input(m,first++) ;

state*=MyConvert::copyto(&number,m[first++]);

state*=MyConvert::copyto(&price,m[first++]);

state*=validate() ;

return (state==0)? 0: 4;}

int Book::output(char* m[], int first)

{m[first++]=autor.ToPchar();

m[first++]=title.ToPchar();

::output(m[first++],number);

::output(m[first++],price);

return 4;

}

Файл Reader.h

class Reader:public Record

{

MyString name;

MyString address;

int number;

int telephon;

public:

Файл Library.h

class Library:public Record

{ Book* b;

Reader* r;

MyString bibl;

MyDate d;

public:

Файл Library.cpp

int Library::equal( const Library& l)

{ return

((l.b!=0)? b->equal(*l.b) :1)&&

((l.r!=0)? r->equal(*l.r) :1)&&

((l.bibl.length()!=0)? bibl.equal(l.bibl) :1)&&

(((l.d.get_day() !=0) && (l.d.get_month() !=0) && (l.d.get_year() !=0))?

d.equal(l.d):1);

}

int Library::cmp(const Library& t) // функция, сравнивающая записи

{int cond;

MyString s(t.bibl);

if ((t.b)&& (cond=b->cmp(*t.b))) return cond;

else if((t.r)&& (cond=r->cmp(*t.r))) return cond;

else if(s.length()!=0&& (cond=bibl.cmp(t.bibl)))return cond;

else if( (cond=d.cmp(t.d)))return cond;

}

int Library::equal(const Obj& t)

{return equal((const Library&) t);}

int Library::cmp(const Obj& t)

{return cmp((const Library&) t);}

int CmpField(Library* l1,Library* l2,char*s)

{if (!strcmp(s,"book")) return l1->b->cmp(*l2->b);

else if (!strcmp(s,"reader")) return l1->r->cmp(*l2->r);

else if (!strcmp(s,"bibl")) return l1->bibl.cmp(l2->bibl);

else if(!strcmp(s,"date")) return l1->d.cmp(l2->d);

}

int Library::CmpField(const Obj& obj,char*s)

{return ::CmpField(this,(Library*)&obj,s);}

int Library::input (char* m[], int first)

{int state=1,f=first;

b=new Book;

r=new Reader;

state*=b->input(m,first) ;

first+=b->CountFields();

state*=r->input(m,first) ;

first+=r->CountFields();

state*=bibl.input(m,first++) ;

state*=d.input(m,first);

first+=3;

state*=validate() ; ;

return (state==0)? 0: first-f ;}

int Library::output(char* m[], int first)

{int f=first;

Book* bb;

Reader* rr;

if(b==0)

{bb=new Book;

first+=bb->output(m,first);

delete bb;}

else first+=b->output(m,first);

if(r==0)

{rr=new Reader;

first+=rr->output(m,first);

delete rr;}

else first+=r->output(m,first);

m[first++]=bibl.ToPchar();

first+=d.output(m,first);

return first-f;

}

В качестве элементов таблицы используется тип Obj*, который обозначен как T. В связи с этим при необходимости вызова функций, декларированных в абстрактном классе InOut, который является производным по отношению к классу Obj, необходимо явное приведение типа.

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

Файл Tabl.h

class Tabl{

vec v;

Iterator cur;

T buf;

public:

Файл Tabl.cpp

int Tabl::input(File f)

{

while(!eof( f ) )

if(((InOut*)buf)->input (f))

insert(buf);

return length();

}

void Tabl::output(File f)

{T* i;

for(i=begin();i!=end();i++)

((InOut*) (*i))->output(f);

}

void Tabl::output(ListView^list,int count)

{list->Items->Clear();

for(T* i=begin();i<end();i++)

((InOut*)(*i))->output(list,count);

}

int Tabl::remove(const T& item)

{T* i;

T* j=begin(); int n=0;

for( i=begin();i<cur;i++)

if(! (*i)->equal(*item))

*j++=*i;

else { (*i)->dispose();

delete (*i);

n++;}

cur=j;

return n;

}

Файл item.h

#pragma once

#include "Obj.h"

typedef Obj* T;

typedef T* Iterator;

Файл algo.cpp

Iterator find( const Iterator& first,const Iterator& last,const T& item)

{Iterator i;

for(i=first;i<last;i++)

if( (*i)->equal(*item)) return i;

return last;

}

Использование одной функции сmp для сравнения записей обладает существенным недостатком. Для проведения сортировки по различным полям необходимо изменять функцию сравнения и перекомпилировать код. Применение указателей на функцию позволяет построить более универсальную программу.

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

Файл Compare.h

class Compare{

public:

virtual int cmp( Obj* a, Obj* b)=0;

};

class Compare1: public Compare {

ParamSort* param;

public: int cmp( Obj* a, Obj* b)

{return Record::CmpParamSort(a,b,param);}

Compare1(ParamSort* p):param(p){}

};

class Predicat{

public:

virtual bool fun( Obj* item)=0;

};

class Predicat1: public Predicat{

InRange* param;

public: bool fun( Obj* item){return ((Record*)item)->InRangeValue(param);}

Predicat1 (InRange* p):param(p){}

};

Файл algo.cpp

Iterator find(const Iterator& first,const Iterator& last,Predicat &pred)

{Iterator i;

for(i=first;i<last;i++)

if(pred.fun(*i)) return i;

return last;

}

void sort(const Iterator& first,const Iterator& last,Compare &Comp )

{

Iterator i;

Iterator j;

T tmp;int k=0;

for(i=first;i<last;i++,k++)

{

for(j=first+1;j<last-k;j++)

if (Comp.cmp(*(j-1),*j))

{tmp=*(j-1);*(j-1)=*j;*j=tmp;}

}

}