Пример построения системы классов



 

Известно, что при объявлении массивов в Си/Си++ количество элементов массива задается константой и в дальнейшем не может быть изменено. При обращении к элементам массив отсутствует контроль выхода за пределы индексов массива, что приводит к трудно обнаруживамым ошибкам в программах. Построим систему классов для обработки динамических массивов, в которые можно добавлять новые элементы и исключить возможность выхода за пределы текущего размера массива. Общие свойства массивов с такими сойствами, не зависящие от типа элементов массива, объединим в классе TBase, а для массивов с различными типами элеменов образуем свои классы. Описания классов объединим в файле заголовков TBASEARR.H, а определения методов приведем в файле TBASEARR.CPP.

// файл TBASEARR.H#include <string.h>#include <iostream.h> class TBase        //базовый класс для массивов всех типов {int size,           //размер элемента    count,          //текущее число элементов    maxCount,   //размер выделенной памяти в байтах    delta;           //приращение памяти в байтах char *pmem;   //указатель на выделенную память int changeSize(); //перераспределение памяти protected:         void* getAddr( ){return (void*) pmem;};   void addNewItem(void*);  //добавление в конец массива   void error(const char* msg){cout <<msg<<endl;}; public:   int getCount() {return count;};       TBase(int s,int m,int d);       TBase();       TBase(TBase&);      ~TBase(); };  /* Массив с элементами типа int */ class TIntArray: public TBase { public:    int getElem(int index); // Значение элемента по индексу   void putElem(int index,int &pe); // Замена значения элемента по индексу   void addElem(int& el);     // Добавление элемента в конец массива   TIntArray& add(TIntArray&); // Сложение двух массивов поэлементно   TIntArray& subtract(TIntArray&); // Вычитание массивов   void printElem(int index); // Вывод значения элемента на экран   void print(); // Вывод на экран всего массива   TIntArray(int m,int d):TBase((int)sizeof(int),m,d){ }; /*Конструктор */   TIntArray(TBase& a):TBase( a ){}; /*Конструктор */  ~TIntArray(); };

Определения методов приведены в файле TBASEARR.CPP:

#include <iostream.h>#include <stdlib.h>#include <constrea.h>#include <tbasearr.h> /* Методы класса TBase */ TBase::TBase(int s,int m,int d):size(s),maxCount(m),delta(d) {char* p;  int k;  count = 0; p = pmem = new char [size * maxCount];  for (k=0; k < maxCount; k++)   { *p = '\0'; p++;} } TBase::TBase():size(1),maxCount(10),delta(1) {char* p;  int k;  count = 0; p = pmem = new char [size *maxCount];  for (k=0; k < maxCount; k++)    { *p = '\0'; p++;} } TBase::TBase(TBase& b):size(b.size),maxCount(b.maxCount),delta(b.delta) { int k;    count = b.count; pmem = new char [size * maxCount];    for (k=0; k < maxCount * size; k++)       { pmem[k] = b.pmem[k];} } TBase::~TBase () { delete [ ] pmem; }

Виртуальные функции

Понятие о “позднем” связывании

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

Описание виртуальных функций

Функция-компонента класса объявляется как виртуальная указанием ключевого слова virtual. Функции-компоненты в производных классах, заменяющие виртуальную функцию базового класса должны объявляться с тем же именем, тем же списком параметров и типом возвращаемого значения, что и соответствующая функция базового класса. Если из производного класса не образуется новых производных классов, ключевое слово virtual в описании функции можно опустить.

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

Виртуальная функция может быть объявлена в форме:

   virtual void print ( ) = 0;

Такая функция называется “чистой” (pure) виртуальной функцией, а объектный тип, содержащий ее объявление, называется абстрактным объектным типом. В программе не могут создаваться экземпляры абстрактных типов, такой тип может использоваться только для образования производных типов, причем в производном типе следует либо снова определить эту виртуальную функцию как чистую, либо обявить ее как обычную виртуальную функцию, выполняющую конкретные действия.

Виртуальные функции особенно полезны, когда к методом класса требуется обращаться через указатель на экземпляр класса, а сам этот указатель имеет тип указателя на базовый класс. Пусть, например, в классе TBase объявлена чистая виртуальная функция print:

class TBase        //базовый класс для массивов всех типов  {int size,       //размер элемента       count,      //текущее число элементов       maxCount,   //размер выделенной памяти в байтах       delta;      //приращение памяти в байтах   char *pmem;     //указатель на выделенную память    int changeSize(); //перераспределение памяти  protected:          void* getAddr( ){return (void*) pmem;};    void addNewItem(void*);  //добавление в конец массива    void error(const char* msg){cout <<msg<<endl;};  public:    int getCount() {return count;};       TBase(int s,int m,int d);       TBase();       TBase(TBase&);      ~TBase();    virtual void print ( ) = 0; // Чистая виртуальная функция };

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

class TIntArray : public TBase { /* Другие методы */   virtual void print ( ); }class TRealArray : public TBase { /* Другие методы */    virtual void print ( ); }

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

TBase *pb; TIntArray aint(5,3); TRealArray areal(4,2);

Тогда для печати массивов могут применяться операторы

pb = &aint; pb->print(); //Печать массива aint pb = &areal; pb->print(); // Печать массива areal

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

Программа:


Дата добавления: 2019-07-15; просмотров: 131; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!