|
Программирование >> Структура ядра и системные вызовы
Применение такого синтаксиса обеспечивает вызов функции-деструктора объектов массива для каждого из них. Если этот массив освобождается операцией delete arrayP; ТО функция-деструктор вызывается только для первого объекта массива. Операции new и delete могут перегружаться в классе. В этом случае при выделении объекта такого класса с помощью операции new и освобождении его посредством операции delete используется экземпляр определенных в данном классе операций. Перегруженные операции new и delete лояжпы объявляться как функции-члены класса. Они рассматриваются как статические функции-члены, не имеющие возможности изменять данные-члены объектов своих классов. В следующем примере объявляются перегруженные операции new и delete в классе date: class date ( int year, month, day; public: date (int a, int b, int c), { year=a, month=b, day=c; }; -dateO {}; перегруженная операция new void* operator new (size t siz ) return::new char[siz]; перегруженная операция delete void operator delete (void* ptr) { ::delete ptr; Перегруженная функция-член new должна принимать аргумент типа slzej, который задает размер выделяемого объекта в байтах. Эта функция возвращает затем адрес вновь выделенного объекта. Перегруженная функция-член delete должна принимать аргумент void*, который указывает на освобождаемый объект. Эта функция не возвращает никакого значения. Пользователи могут реализовать тела функций-членов new и delete так, как считают нужным. 2.10. Перегрузка операций С++ позволяет пользователям определять стандартные встроенные операции для работы с классами. Это дает возможность использовать объекты классов так, как будто они содержат данные, относящиеся к базовому типу. Встроенные операции + , * и [] для объектов классов смысла не имеют, если только пользователи не перегрузят их явно в своих классах.
В таблице показаны знаки операций С++, которые можно перегружать в классах. При этом значение, очередность выполнения, число операндов перегрузкой не отменяются и, следовательно, операции + , * и & могут определяться как унарные или бинарные. Более того, при перегрузке все префиксные и постфиксные операции ++ и - рассматриваются как префиксные. Все перегруженные функции операций должны принимать в качестве аргумента минимум один объект класса. Они могут объявляться как дружественные функции или как функции-члены. При этом операции и , которые не требуют, чтобы левый операнд был объектом класса, должны определяться как дружественные функции, а операции (] , = , -> , () и += , которые требуют, чтобы левый операнд был объектом класса,- как функции-члены. Перегрузка операций демонстрируется в следующем примере: source module: overload.С ♦include <iostream.h> ♦include <string.h> class sarray int num; char *data; public: Mj ;>.: ;;, sarray( const char* str ) ГЛ конструктор .{ . , ; . num = strlen(str)+1; data = new char[num]; strcpy ( data, str ) > -sarrayO { delete data; ); chars operator[) {int idx ) деструктор { if (idx>=0 && idx<num) return data(idx]; else cerr Invalid index: idx endl; return data[0]; const char*-operator=( const char*--stir-) if (strlen(str)) . ( . . , delete data; num = strlen (str).+ l> data = new char[num]; strcpy (data, str ); . return str; const sarrays operator=( const sarrays ob) { < rif {istrlen{obj .data)) delete data; num = strlen(obj.data)+1; data = new char[num]; strcpy (data, obj.data ); ).. . . . return obj;, int operator == ( const char* str ) return (str s& data) ? strcmp(data, str) int operator < ( const char* str) return (strcmp(data,str) < 0) ? 1 : 0; int operator >,j{, const char* str )< Д;-. . . return {strcmp(data,str) > 0) ? 1 : 0; fri nd ostreams operator (©streams os, sarrays obj) return OS (obj.data ? obj.data : Nil ) ; int main 0 ( sarray A( Hello ), в( world ); cout A: A endl; A = Bad ; A(0] = T; cout A: A endl; A = B; COцt , A: A(l] endl; cout <i: A < Ё: (A < Two ) endl; return 0; операция << операция = операция (] операция =array& В этом примере определяется класс sarray, который организует массив символов для каждого из своих объектов. Преимущество использования этого класса состоит в том, что каждый из его объектов динамически настраивает свой буфер для хранения любой строки символов. Более того, объектами можно оперировать с помощью операций << , [] , = и < , которые объявлены как перегруженные функции. Все они делают объекты sarray более доступными для понимания и использования, избавляют пользователей от необходимости манипулировать массивом на более низком уровне. Компиляция и пробное выполнение этой программы дают следующие результаты: % СС overload.с % а.out А: Hello А: Tad А: о А < В: О 2.11. Шаблоны функций и шаблоны классов Шаблоны функций и шаблоны классов позволяют пользователям создавать родовые функции и классы, работающие с разными типами данных и классами. После их кодирования и тестирования пользователи могут создавать разные экземпляры этих функций и классов для конкретных типов данных и (или) классов. Таким образом, применение шаблонов функций и классов позволяет добиться существенной экономии времени разработчика. Еще одно преимущество шаблонов - сокращение (в расчете на одну программу) числа уникальных функций и имен классов, требующих определения. Это ускоряет процесс компиляции и снижает вероятность конфликтов имен в пользовательских программах. Экземпляр шаблона функции или класса создается при первом использовании или при указании адреса шаблона. Он не имеет имени и уничтожается, как только перестает быть нужным. 2.11.1. Шаблоны функций Синтаксис объявления шаблона функции представлен ниже: template <список формальных параметров> <возвращаемое значение> <имя функции> ( <список аргументов> ) <тело> Шаблон функции, меняющей местами содержимое двух объектов, определяется следующим образом: template <class т> void swap ( TS left, T& right ) < T temp = left; left = right; right = temp; После объявления подобной шаблонной функции пользователи могут создавать объекты специализированных экземпляров этого класса для различных типов данных. Покажем, как это делается: main О { int а = 1, b = 2; swap(a,b); создает экземпляр swap{int,int) double aa = 101.0, bb = 25.0; swap(aa,bb); создает экземпляр swap(double,double) Список формальных параметров заключается в символы < и >. Он содержит формальные параметры типа, отделенные друг от друга запятыми. Этот список не может быть пустым. Каждый формальный параметр начинается с ключевого слова class, а далее следует идентификатор типа. Например, правильным будет такое объявление: template <class Т, class U> void foo(T*, U); Следующее определение - неверное, поскольку перед идентификатором типа и нет ключевого слова class: template <class Т, U> void fool(TS); ошибка. Должно быть class U Параметр может встречаться в списке только один раз. Представленное далее объявление содержит ошибку, поскольку задает class Г дважды: template <class Т, class Т> void fool(Т&); ошибка Каждый параметр должен быть указан в списке аргументов шаблона функции хотя бы один раз. Следующее объявление ошибочно, потому что в списке аргументов функции отсутствует идентификатор типа U: template <class Т, class U> U fool{T&); ошибка Правильным будет использовать идентификатор типа U в списке аргументов функции: template <class Т, class U> U fool(T&,U*); нормально Шаблон функции может объявляться как внешний {extern), статишский (static) или встроенный (inline). Этот спецификатор ставится после списка параметров, перед спецификацией типа возвращаемого значения. Ниже объявляются встроенная и внешняя функции: template <class Т> inline void foo(T* tobj, int size) {...} template <class T> extern int fooA(T& tobj); Шаблон функции можно перегружать при условии, что в сигнатуре объявлений будут указаны разный тип или различное количество аргументов. Все приведенные ниже объявления допустимы: template <class Т> Т min(Т tl, Т t2); template <class Т> Т min(T* tl, Т t2, т t3); template <class T> T min(T tl, int t2); Однако следующие два объявления являются неверными, потому что идентификаторы типа параметров нельзя использовать для различения перегруженных шаблонов функции: template <class Т> т min(Т tl, т t2) ; template <class U> U min(U tl, U t2); ошибка! И, наконец, можно определить специализированные шаблонные функции. При установлении порядка вызова функций специализированные функции имеют более высокий приоритет, чем родовые шаблоны функций. Например, в следующей программе в первом вызове min в функции main используется специализированная версия min, которая принимает аргументы типа char*, а второй вызов min создает экземпляр шаблона функции min для данных типа double: template <class Т> Т min{T tl, Т t2) ( return (tl < t2) ? tl : t2; специализированная версия minО char* min(char* tl, char* t2) ( return (strcmp(tl,t2) < 0) ? tl : t2;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |