Дружественные” (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, независимо от закрепленного за ними уровня доступа.
В теории объектно-ориентированного программирования считается, что при хорошо спроектированной системе классов не должно быть необходимости в дружественных функциях, однако в ряде случаев их использование упрощает понимание и последующие модификации программы.
Статические компоненты класса
Описатель static в С++ имеет различное назначение в зависимости от контекста, в котором он применен.
|
|
Переменные и функции, объявленные вне класса и вне тела функции с описателем static, имеют область действия, ограниченную файлом, в котором они объявлены.
Переменные, объявленные как static внутри функции, видимы только внутри этой функции, но сохраняют свои значения после выхода из функции и инициализируются только при первом обращении к функции.
Компоненты класса также могут объявляться с описателем static, такие компоненты - данные являются общими для всех экземпляров объектов этого класса и размещаются в памяти отдельно от данных объектов класса. Доступ к static - компонентам класса возможен по имени, уточненному именем класса (именем типа) или именем объекта этого класса, причем к static - компонентам класса можно обращаться до создания экземпляров объектов этого класса. Статическое данное - член класса должно быть обязательно инициализировано вне описания класса:
class TBase //базовый класс для массивов всех типов { static int nw; int size, //размер элемента count, //текущее число элементов maxCount, //размер выделенной памяти delta; //приращение памяти /* Другие компоненты класса TBase */ } int TBase::nw =1; /* Инициализация статической компоненты класса */Статические компоненты - функции могут вызываться до создания экземпляров объектов этого класса и поэтому имеют доступ только к статическим данным класса:
|
|
Переопределение (перегрузка) операций
В языках программирования определена семантика операций, выполняемых над базовыми (предопределенными) типами данных, например, если x, y и z - переменные типа float, то запись x = y + z; предполагает интуитивно очевидные действия, сложение x и y и присваивание переменной z полученной суммы.
Желательно было бы и для типов, определяемых в программе, в том числе для классов, определить семантику и алгоритмы операций сложения, вычитания, умножения и т.д., чтобы иметь возможность вместо вызова соответствующих функций записывать просто x + y и в случае, когда x и y являются объектами некоторых классов. В C++ это достигается переопределением имеющихся в языке операций для других типов данных.
Переопределенная операция объявляется так:
тип_результата operator знак_операции (формальные параметры) { описание_алгоритма_выполнения_операции }Например:
|
|
Полное определение этих операций для объектов класса 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 - компонентами класса.
В остальном к переопределяемым операциям предъявляются те же требования, что и к функциям.
Шаблоны функций и классов
Шаблоны функций
Часто встречаются функции, реализующие одни и те же действия для аргументов различных типов. Например, сортировка массива по возрастанию его элементов может выполняться одним и тем же методом и для данных типа 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; }Если в программе будут объявлены массивы
Дата добавления: 2019-07-15; просмотров: 154; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!