Рефераты. Препроцессорные средства в C и С++

Если базовый класс private, то в производном классе public и protected компоненты базового класса доступны для функций производного класса, но для следующего производного класса они будут считаться private, т.е. будут недоступны, private-компоненты базового класса недоступны в производных классах.

Конструктор производного класса должен вызывать конструктор своего базового класса:

class TBase

    { public: TBase( int s, int m, int d);

      /* Другие компоненты класса TBase */

    }

class TVect : public TBase

    { public: TVect ( int k, int s, int m int d): TBase(s, m, d)

                { /* инициализация остальных компонент TVect */};

    }

 

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


Известно, что при объявлении массивов в Си/Си++ количество элементов массива задается константой и в дальнейшем не может быть изменено. При обращении к элементам массив отсутствует контроль выхода за пределы индексов массива, что приводит к трудно обнаруживамым ошибкам в программах. Построим систему классов для обработки динамических массивов, в которые можно добавлять новые элементы и исключить возможность выхода за пределы текущего размера массива. Общие свойства массивов с такими сойствами, не зависящие от типа элементов массива, объединим в классе 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; }

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;

Страницы: 1, 2, 3, 4, 5, 6, 7



2012 © Все права защищены
При использовании материалов активная ссылка на источник обязательна.