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

   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;

    public:

      TPoint& operator+=( const TPoint& adder );

      TPoint& operator-=( const TPoint& subber );

      friend TPoint operator - ( const TPoint& one, const TPoint& two);

      friend TPoint operator + ( const TPoint& one, const TPoint& two);

      friend int operator == ( const TPoint& one, const TPoint& two);

      friend int operator != ( const TPoint& one, const TPoint& two);

   };

Полное определение этих операций для объектов класса TPoint имеет вид:

inline TPoint& TPoint::operator += ( const TPoint& adder )

   {  x += adder.x;    y += adder.y;    return *this;}  

  

   inline TPoint& TPoint::operator -= ( const TPoint& subber )  

   {  x -= subber.x;    y -= subber.y;    return *this;}

Остальные операции определяются аналогичным образом.

Пусть в программе имеются объявления:

TPoint x(12,3), y(21,30), z(18,30);

Тогда можно записать:

   x +=y;  y-=z;   TPoint r = x + z:

Общие правила переопределения операций сводятся к следующему:

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

- При переопределении сохраняется приоритет исходной операции т.е. операция + будет выполняться раньше операции = и т.д.

- При переопределении не наследуются свойства коммутативности и ассциативности, т.е. результат выражения х + y - z может отличаться от результата выражения y - z + x и зависит от того, как определены соответствующие операции.

- Не допускается переопределение операций . (точка), .* ( точка -звездочка, обращение к указателю на компоненту класса или структуры), :: (разрешение контекста), а также операции # и ##, используемые при препроцессорной обработке.

- Переопределяемые операции = (присваивание), () (функция), [ ] (индекс), -> (обращение к компоненте класса по указателю) всегда должны быть компонентами класса и не могут быть static.

- Переопределяемые операции new и delete должны быть static - компонентами класса.

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

5. Шаблоны функций и классов

 

5.1. Шаблоны функций


Часто встречаются функции, реализующие одни и те же действия для аргументов различных типов. Например, сортировка массива по возрастанию его элементов может выполняться одним и тем же методом и для данных типа int и для данных типа double. Различие состоит только в типах параметров и некоторых внутренних переменных.

В более поздние версии С++ включено специальное средство, позволяющее параметризовать определение функции, чтобы компилятор мог построить конкретную реализацию функции для указанного типа параметров функции. Параметризованное определение функции строится по схеме:

template < class имя_класса >

          Заголовок функции

      { /* Тело функции */ }

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

Параметризованное определение функции сортировки массива методом перестановок может быть построено следующим образом:

template <class T >

  void sort ( T a[ ], int  n )

   {   T  temp;

      int sign;

      for ( int k = 0; k > n; k++)

       { sign = 0;

         for ( i = 0; i <n - k; i++)

          if ( a [ i ] > a [ i + 1])

             { temp = a [ i ];

               a[ i ] = a[ i + 1 ];

               a[ i + 1 ] = temp; sign++;

              }

          if ( sign == 0 ) break;

        }

       return;

  }

Если в программе будут объявлены массивы

int aint [10];

double afl [20];

и установлены значения элементов этих массивов, то вызов функции

  sort ( aint, 10 );

обеспечит вызов sort для упорядочения массива целых, а вызов функции

  sort ( afl , 20 )

обеспечит вызов sort для упорядочения массива с элементами типа double.

Если элементами массива являются объекты какого-либо определенного программистом класса, для которого определена операция отношения >, то функция sort может быть вызвана и для такого массива. Разумеется, в объектном коде программы будут присутствовать все варианты реально вызывамой функции sort. Параметризация функции сокращает объем исходного текста программы и повышает его надежность.

В описателе template можно указывать несколько параметров вида class имя_типа, а также параметры базовых типов. Например, функция

template < class T1, class T2 >

void copy ( T1 a[ ], T2 b[ ], int n)

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

          a[ i ] = b [ i ] ;

      }

копирует первые n элементов массива b типа T2 в первые n элементов массива a типа T1. Разумеется, программист несет ответственность за то, чтобы такое копирование было возможным.

5.2. Шаблоны классов

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

  template <class T >

        class  описание класса

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

В качестве примера приведем описание класса stack, предназначенного для построения стеков фиксированного максимального размера с элементами произволного типа.

enum BOOLEAN ( FALSE, TRUE );

template <class Type >

class stack

  { private:

    enum ( EMPTY = -1 );

    Type*     s;     /* Указатель на массив стека  */

     int      max_len;   /* Максимальная длина стека */

     int      top;     /* Индекс элемента в вершине стека */

    public:

     stack ( ) : max_len ( 100 ) /* конструктор без параметров */

      { s = new Type [ 100 ]; top = EMPTY; }

     stack ( int size ) : max_len( size ) /* Второй конструктор  */

      { s = new Type [ size ]; top = EMPTY; }

     ~stack ( ) { delete [ ] s;  }    /* Деструктор   */

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



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