p align="left"> 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; } 4.5 Виртуальные функции4.5.1. Понятие о “позднем” связыванииПри описании объектных типов функции, имеющие сходное назначение в разных классах, могут иметь одинаковые имена, типы параметров и возвращаемого значения. При обращении к такой функции с указанием имени объекта компилятору известно, какая из одноименных функций требуется. В то же время к объектам производного типа можно обращаться по указателю на базовый тип и тогда на этапе компиляции нельзя установить, функция какого из производных типов должна быть вызвана. В ходе выполнения программы требуется проверять, на объект какого типа ссылается указатель и после такой проверки вызывать требуемую функцию. Эти действия называют “поздним” связыванием, в отличие от “раннего” связывания, при котором уже на этапах компиляции или редактирования связей можно установить адрес точки входа вызываемой функции. В объектно-ориентированных языках программирования для решения этой проблемы применяются виртуальные методы. 4.5.2. Описание виртуальных функцийФункция-компонента класса объявляется как виртуальная указанием ключевого слова 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. Для краткости ограничимся в описании каждого животного его кличкой и типовым излаваемым животным звуком с возможностью вывода на экран списка кличек и представления издаваемых ими звуков. Программа: #include <iostream.h> struct Pet // Базовый класс { char *name; virtual void speak() = 0; Pet( char *nm){name=nm;} } struct Dog : public Pet { virtual void speak( ) { cout<<name<<“ говорит “”<<“ Ав - ав”<<endl; }; Dog(char *nm): Pet(nm) { }; }; struct Cat : public Pet { virtual void speak( ) { cout<<name<<“ говорит “ <<“ Мяу-Мяу”<<endl; Cat(char *nm): Pet(nm) { }; } int main () { Pet *mypets[ ] = { new Dog(“Шарик”), new Cat(“Мурка”), new Dog(“Рыжий “)}; // Список животных const int sz = sizeof( mypets)/ sizeof( mypets [ 0 ]); for ( int k = 0: k < sz; k++) mypets [ k ]->speak(); return 0; } 4.6. “Дружественные” (friend) функцииФункция, объявленная в производном классе, может иметь доступ только к защищенным (protected) или общим (public) компонентам базового класса. Функция, объявленная вне класса, может иметь доступ только к общим (public) компонентам класса и обращаться к ним по имени, уточненному именем объекта или указателя на объект. Чтобы получить доступ к личным компонентам объектов некоторого класса Х в функции, не имеющей к ним доступа, эта функция должна быть объявлена дружественной в классе X: class X { friend void Y:: fprv( int, char*); /* Другие компоненты класса X */ } Можно объявить все функции класса Y дружественными в классе X; class Y; class X { friend Y; /* Другие компоненты класса X */ } class Y { void fy1(int, int); int fy2( char*, int); /* Другие компоненты класса Y */ } Дружественной может быть и функция, не являющаяся компонентой какого-либо класса, например, class XX { friend int printXX ( ); /* Другие компоненты класса ХХ */ } Здесь функция printXX имеет доступ ко всем компонентам класса XX, независимо от закрепленного за ними уровня доступа. В теории объектно-ориентированного программирования считается, что при хорошо спроектированной системе классов не должно быть необходимости в дружественных функциях, однако в ряде случаев их использование упрощает понимание и последующие модификации программы. 4.7. Статические компоненты классаОписатель static в С++ имеет различное назначение в зависимости от контекста, в котором он применен. Переменные и функции, объявленные вне класса и вне тела функции с описателем static, имеют область действия, ограниченную файлом, в котором они объявлены. Переменные, объявленные как static внутри функции, видимы только внутри этой функции, но сохраняют свои значения после выхода из функции и инициализируются только при первом обращении к функции. Компоненты класса также могут объявляться с описателем static, такие компоненты - данные являются общими для всех экземпляров объектов этого класса и размещаются в памяти отдельно от данных объектов класса. Доступ к static - компонентам класса возможен по имени, уточненному именем класса (именем типа) или именем объекта этого класса, причем к static - компонентам класса можно обращаться до создания экземпляров объектов этого класса. Статическое данное - член класса должно быть обязательно инициализировано вне описания класса: class TBase //базовый класс для массивов всех типов { static int nw; int size, //размер элемента count, //текущее число элементов maxCount, //размер выделенной памяти delta; //приращение памяти /* Другие компоненты класса TBase */ } int TBase::nw =1; /* Инициализация статической компоненты класса */ Статические компоненты - функции могут вызываться до создания экземпляров объектов этого класса и поэтому имеют доступ только к статическим данным класса: class X { static int sx1,sx2; static void fsx ( int k); int x1,x2; /* Другие компоненты класса X */ } int X::sx1 = 1; int X::sx2 = 2; int main () { .......... X:: fsx( 3 ); .............. } 4.8. Переопределение (перегрузка) операцийВ языках программирования определена семантика операций, выполняемых над базовыми (предопределенными) типами данных, например, если x, y и z - переменные типа float, то запись x = y + z; предполагает интуитивно очевидные действия, сложение x и y и присваивание переменной z полученной суммы. Желательно было бы и для типов, определяемых в программе, в том числе для классов, определить семантику и алгоритмы операций сложения, вычитания, умножения и т.д., чтобы иметь возможность вместо вызова соответствующих функций записывать просто x + y и в случае, когда x и y являются объектами некоторых классов. В C++ это достигается переопределением имеющихся в языке операций для других типов данных. Переопределенная операция объявляется так: тип_результата operator знак_операции (формальные параметры) { описание_алгоритма_выполнения_операции } Например: class TPoint { int x,y;
Страницы: 1, 2, 3, 4, 5, 6
|