|
Программирование >> Структура ядра и системные вызовы
int mainO (flftr. char* ptr = min ( С++- , UNIX ); double X = min(2.0, 3.0); min (char*,- char*) min(T,T) 2.11.2. Шаблоны классов Объявление шаблона класса имеет такой формальный синтаксис: template <список формальных параметров> class <имя класса> { <объяБление> За ключевым словом template в объявлении шаблона класса следует список формальных параметров, затем имя класса и его тело. Список формальных параметров заключается в символы < и >, и параметры в нем отделяются друг от друга запятыми. Вот пример объявления шаблона класса: template <class Т, int len> class foo ( . , T list[len]; }; Как и в объявлении шаблона функции, каждый параметр, объявленный в списке формальных параметров, должен быть использован в объявлении соответствующего шаблона класса хотя бы один раз. Более того, при каадой ссылке на имя шаблона класса должен указываться и список параметров, заключенный в угловые скобки (кроме случая, когда это делается внутри объявления класса). Объявим шаблон класса Array, который содержит массив типа Тс количеством элементов len. Параметры Г и len должны указываться при создании экземпляров этого шаблона. Обратите внимание на то, что в определении функции-конструктора указаны имя класса и список параметров: template <class Tv int len>, class Array { public: Array 0; -Array 0 protected: T list[len]; template <class TT, int len> inline Array<TT,len>::Array0 Системное программирование на C+-f для UNIX Чтобы создать объект для специализированного экземпляра шаблона класса, пользователи указывают имя класса и фактические данные для списка параметров, заключенного в символы < и >. Так, чтобы создать объект класса Array, который содержит целочисленный массив из 100 элементов, нужно дать следующее его определение: Array<int, 100> foo; foo - это объект Шаблоны классов могут быть порождены от шаблонных и нешаблонных базовых классов. Соотношение типов и подтипов между порожденными и базовыми открытыми шаблонами классов сохраняется при условии, что они имеют фактические параметры одного типа. Объявим шаблон подкласса Array super, порожденный от шаблонов классов 67 и 62 Переменная foo определяется как экземпляр класса Array super, а ptr - это указатель на экземпляр класса Ы. Поскольку экземпляры шаблонов bl<int> и Array super<int> имеют параметры одного типа, то они совместимы и, следовательно, указателю ptr можно присвоить адрес foo. Однако экземпляры шаблонов bl<double> и Array super<int> весовместкмы, поэтому присваивание адреса foo указателю ptr2 будет ошибкой., template <class Туре> class Array super: public bl<Type>, public :Ь2<Туре> {.. Array super<int> foo; . . bl<int> *ptr = &foo; , . bl<double> *ptr2 = &foo; . .); / правильно 7/ ошибка Внутри шаблонов классов могут объявляться дружественные функции и классы. Каждая из функций может быть: * функцией или классом, объявленными без шаблона; шаблоном; специализированным экземпляром шаблона функции. Сказанное относится и к дружественному классу, объявляемому внутри шаблона. Если дружественная функция и дружественный класс представляют собой шаблоны, то все экземпляры данной функции или данного класса являются дружественными для шаблонов, внутри которых они объявлены. С другой стороны, если дружественная функция (или класс) является специализированным экземпляром шаблона, то дружественным для объявляемого шаблона будет только этот экземпляр. Данные понятия можно проиллюстрировать следующим образом: template <class U> class Container ( общий шаблон дружественного класса template <class Т> friend class general class; общий шаблон дружественной функции template <class UT> friend general func ( <UT>&, int );... Глава 2. Обзор языка С++ дружественным является только экземпляр этого же типа U класса Array friend class Array<U>; дружественным является только экземпляр этого же типа U функции friend ostreamS operator<< (osterams, Container<U>s); нешаблонный дружественный класс friend class dates; , нешаблонная дружественная функция frien void foo {); В этом примере шаблон класса Container имеет формальный параметр типа и. Класс Container имеет несколько дружественных функций и классов; из них классы dates и foo являются дружественными для всех специализированных экземпляров класса Container. Шаблон класса Array и перегруженная операция << являются дружественными для специализированных экземпляров класса Container, если они используют параметры одного типа. Такик* образом, /4/га></ /> - дружественный класс для Container<int>, но Array<dou-b/e> таковым не является. Наконец, все специализированные экземпляры шаблона класса general class и шаблона функции general June - дружественные для всех специализированных экземпляров класса Container Для шаблона класса могут определяться специализированные функции-члены и шаблоны классов. Однако все специализированные экземпляры можно определять только после объявления шаблона класса. Кроме того, в специализированном шаблоне класса должны быть определены все функции-члены шаблона того класса, на котором он построен. В представленном ниже примере объявляются шаблон класса Array, затем специализированный конструктор класса Array для типа данных double и, наконец, определен специализированный класс Array типа char*: template <class Т> class Array 1; 1; public: Array(int sz) { ar=new T[size=sz]; -Array{) { delete [sz] ar;); T& operator[](int) { return ar{i]; .protected: T* ar; int size; специализированный конструктор типа double Array<double>::Array{ int size ){...} определение специализированного класса Array class Array<char*> - . !,i У i public: Array(int sz) { ar=new char[size=sz]; ),; -Array{) { delete [] ar; ); chars operator [ J (int i) { return ar(il; ) protected: char* ar; int size; В шаблоне класса могут объявляться статические данные-члены. В каждом специализированном экземпляре такого шаблона имеется собственный набор статических данных-членов. Ниже объявляется шаблон класса Array с двумя статическими данными-членами: Array<T>::pool и Array<T>::pool sz. Эти статические переменные по умолчанию инициализируются соответственно в О и 100 для всех специализированных экземпляров класса Array: template <class Т> class Array i public: Array (int sz) { ar = new char [sizep=3z]; }} -Array0 (delete [sz] ar; ); void *operator new(size t); void operator delete (void*, si2e t) < protected: Qj :n,] char* ar; int size; static Array* pool; static const int pool sz; ); template <class T> Array<T>* Array<T>;:pool = 0; template <class T> const int Array<T>::pool sz = 100; Можно определить специализированный экземпляр статических данных-членов класса и задать для них уникальные начальные значения. Так, переменные Array<char>::poot и Array<char>::pool sz для экземпляра char класса Array определяются следующим образом: Array<char>* Array<char>::роо1 = new char[1000]; Array<char>* Array<char>::pool sz = 1000; Следует также сказать, что доступ к статическим данным-членам шаблона класса может осуществляться только через специализированный экземпляр этого класса. Так, из представленных ниже трех операторов первый не верен, поскольку обращается непосредственно к Аггау< Т>::роо1, а обращения к Array<char>::pool и Array<int>::pool sz являются корректными: cout Array<T>::роо1 endl; Array<char>* ptr = Array<char>::pool; int X = Array<int>::pool sz; Ошибка правильно правильно Нешаблонная функция может манипулировать объектами специализированных экземпляров шаблонов классов, тогда как шаблонная функция может использовать объекты либо конкретного экземпляра, либо общего шаблона класса. В приведенном ниже примере foo - нешаблонная функция; следовательно, она может работать с объектами специализированного экземпляра (в данном случае Army<int>) класса Array. Шаблонная функция foo2 может манипулировать объектами любого экземпляра класса Array, если они имеют параметры одаого типа (т.е. функция foo2<int> может в качестве аргумента принимать объект типа Array<int>): void foo(Array<int>& Aobj, int size ) :i Array<int> *ptr = SAobj,-; r\: ... . template <class T> extern void foo2 ( Array<T>&, Array<int>& ]; 2.12. Обработка исключительных ситуаций В ANSI/ISO С++ есть стандартный метод обработки исключительных ситуаций, с помощью которого все приложения реагируют на отклонения, возникающие в ходе выполнения программ. Наличие такого метода упрощает работу по созданию приложений и обеспечивает согласованность в их функционировании. Исключительная ситуация - это состояние ошибки, обнаруженное в программе в ходе ее выполнения. Исключительная ситуация генерируется при помощи оператора throw, а функция-обработчик исключительных ситуаций перехватывается определяемым пользователем блоком-ловушкой, который находится в этой же программе. Если блок-ловушка не прекращает выполнение программы, то управление передается в позицию, находящуюся сразу же за блоком-ловушкой, а не за оператором throw. Кроме того, исключительная ситуация может генерироваться только программным кодом, выполняемым прямо или косвенно в блоке try. Таким образом, обработка исключительных ситуаций требует специального структурирования пользовательской программы, предусматривающего наличие области кода, где может возникнуть исключительная ситуация, и одного или нескольких блоков-ловушек для ее обработки. Механизмы обработки исключительных ситуаций в С++ синхронны; генерируются исключительные ситуации в пользовательских приложениях через явные операторы throw. Этим они отличаются от асинхронных исключительных ситуаций, генерируемых такими событиями, как нажатие пользователем клавиши на клавиатуре. Асинхронные исключительные ситуации непредсказуемы, т.е. возникают где угодно и когда угодно, и могут обрабатываться с помощью функции signal (см. главу 9). Рассмотрим пример обработки иркл)Юч;ительной ситуации в С++: source module: simple exception .С ♦include <iostream.h> j;..; main( int argc, char* argvl]j .;, ,. try { if (argc===l] throw Insufficient no. of argument ; while (-argc > 0) cout argc : << argv[argc] endl; cout Finish argv[0] endl; return Of ; . , ], . .... i -r- catch (const char* msg ) ( cerr exception: msg endl; cout main: continue here after exceptionXn ; return 1; Здесь нормальный код функции main заключен в блок try. В этом блоке проверяется значение argc. Если оно равно 1, возникает исключительная ситуация, для генерации которой выполняется оператор throw. Если же значение argc больше 1, то выполняется цикл while, в котором в обратном порядке выводятся все аргументы командной строки, и программа завершается через оператор return 0. Оператор throw имеет следующий синтаксис: throw <выражение>; где <выражение> - это любое выражение С++, которое при вычислении дает значение одного из базовых типов данных С++ или объект класса. Значение этого выражения используется для выбора блока-ловушки, которому соответствует тип аргумента или который совместим с типом данных выражения. Если оператор throw выполняется, то остальные операторы в блоке try пропускаются и управление передается в выбранный блок-ловушку. В функции можно задавать один или несколько блоков-ловушек. Они должны указываться сразу после блока try. Каждый блок-ловушка начинается с ключевого слова catch, за которым следует спецификация типа исключительной ситуации, заключенная в круглые скобки. После спецификации следует один или несколько операторов блока, заключенных в фигурные скобки. Следует отметить, что хотя блок-ловушка выглядит как определение функции, функцией он не является. Это просто набор операторов С++, которые объединены в группу для каждого типа исключительных ситуаций. Исходя из типа исключительной ситуации, оператор throw выбирает блок-ловушку для выполнения, а сам тип исключительной ситуации определяется значением <выражение> оператора throw. Данное значение, как правило, передает дополнительную информацию о генерируемой исключительной ситуации.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |