p align="left"> 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; } /* Деструктор */ void reset ( ) { top = EMPTY; } /* Очистить стек */ void push ( Type c ) { s [ ++top ] = c; } Type pop ( ) { return (s [top--] } Type top_of ( ) { return ( s [top ] } BOOLEAN empty ( ) { return BOOLEAN ( top == EMPTY ) } BOOLEAN full ( ) { return BOOLEAN ( top == max_len ) } }; Следует отметить, что в этом примере с целью сокращения исходного текста не предусмотрен контроль выхода за пределы стека в методах push и pop. Чтобы создать экземпляр параметризованного объектного типа, нужно уточнить имя типа значением параметра в угловых скобках: stack < int > stack_of_int (50); /* Стек на 50 элементов типа int */ stack < myClass > stmc (20); /* Стек на 20 элементов типа myClass */ В приведенном примере все компоненты-функции определены в описании класса. Когда полное определение функции-члена класса задается вне описания класса, оно должно уточняться описателем template. Например, если бы метод top_of был определен вне описания класса, определение имело бы вид: template < class Type > Type top_of ( ) { return s [ top ];} Отметим некоторые специфические черты описаний параметризованных классов. Если в параметризованном классе определены friend-функции, то когда такая функция не зависит от параметра, будет использоваться единственная friend-функция для всех значений параметра, а когда friend-функция зависит от параметра, будет использоваться своя friend-функция для каждого значения параметра. Если в параметризованном классе имеются статические (static) компоненты, то для каждого значения параметра будет использоваться свой экземпляр статической компоненты. 6. Классы для ввода-вывода потоков6.1. Система классов ввода-выводаСистема ввода-вывода С++ основывается на концепции потоков данных, поток представляет собой, с одной стороны, последовательность данных, с другой стороны поток рассматривается как переменная некоторого объектного типа. Это позволяет вынести общие свойства и операции процессов ввода-вывода в определения базовых классов. Ввод-вывод из файлов и консоли, как правило, выполняется с использованием буфера и получение данных программой или вывод данных сводится к пересылке данных из одной области памяти в другую. Реальное обращение к внешним устройствам происходит только при исчерпании данных в буфере (при вводе) или при заполнении буфера (при выводе). Система классов ввода-вывода С++ использует два базовых класса: класс ios и класс streambuf. В классе ios определены данные, характеризующие состояние потока, и функции, позволяющие получить доступ к информации о состоянии потока или изменить его состояние. Состояние потока определяется набором битовых флагов, для обращения к отдельным флагам в классе ios описаны перечислимые константы: - биты состояния (статуса) потока enum io_state { goodbit = 0x00, // никакие биты не установлены, все хорошо eofbit = 0x01, // конец файла failbit = 0x02, // ошибка в последней операции ввода/вывода badbit = 0x04, // попытка выполнить неверную операцию hardfail = 0x80 // неисправимая ошибка }; - биты режима использования потока (режима ввода/вывода) enum open_mode { in = 0x01, // поток открыт для чтения out = 0x02, // поток открыт для записи ate = 0x04, // перейти в конец файла при открытии app = 0x08, // режим добавления в конец файла trunc = 0x10, // усечение существующего файла nocreate = 0x20, // ошибка открытия файла, если он не существует noreplace = 0x40, // ошибка открытия, если файл существует binary = 0x80 // двоичный (не текстовый) файл }; - флаги направления позиционирования в потоке enum seek_dir { beg=0, cur=1, end=2 }; - флаги - манипуляторы управления вводом/выводом enum { skipws = 0x0001, // пропускать пробелы при вводе left = 0x0002, // выравнивание влево при выводе right = 0x0004, // выравнивание вправо при выводе internal = 0x0008, // пробел после знака или основания системы счисления dec = 0x0010, // преобразование в десятичную систему счисления oct = 0x0020, // преобразование в восьмеричную систему счисления hex = 0x0040, // шестнадцатеричное преобразование showbase = 0x0080, // использовать индикатор системы счисления при выводе showpoint = 0x0100, // указывать десятичную точку при выводе //(в числах с плавающей точкой) uppercase = 0x0200, // прописные буквы при шестнадцатеричном выводе showpos = 0x0400, // добавлять '+' для положительных целых scientific= 0x0800, // применять нотацию вида 1.2345E2 fixed = 0x1000, // применять нотацию вида 123.45 unitbuf = 0x2000, // очищать все потоки после вставки в поток stdio = 0x4000, // очищать stdout, stderr после вставки в поток
Страницы: 1, 2, 3, 4, 5, 6
|