КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Листинг 4.11. FTEMPLAT.CPP (использование шаблонов функций)
Else Else Листинг 4.10. MINMAX.H Шаблонные функции Шаблоны Подобно тому как класс представляет собой схематическое описание построения объекта, так и шаблон представляет собой схематическое описание построения функций и классов. Шаблоны особенно полезны в библиотеках классов, которыми пользуются многие программисты. Шаблоны, иногда также называемые параметризованными типами, указывают лишь спецификации для функций и классов, но не детали настоящей реализации. Шаблоны функций описывают общие свойства функций, подобно рецепту приготовления пирожного. Шаблоны функций, обычно объявляемые в заголовочном файле, имеют следующий общий вид:
template<class T> void f(T param){ // Тело функции} //Т — определяемое пользователем //имя функции
Шаблонная функция начинается строкой template<class Т>, указывающей компилятору, что Т — определяемое пользователем имя функции. (Вы можете заменить Т на любое другое имя, если захотите.)
Необходим, по крайней мере, один параметр Т для передачи функции данных для обработки. Можно также задать указатель (Т* param) или ссылку (Т& param).
Функция может объявлять несколько параметров и возвращать значение типа Т: template<class T> T f(int а, Т b) { // Тело функции} //ф-ция f() возвращает значения типа //Т и имеет два параметра — целое с //именем а и неопределенный объект //Т с именем б.
В этой версии шаблонная функция f() возвращает значения типа Т и имеет два параметра — целое с именем а и неопределенный объект Т с именем Ь.
Пользователи шаблона укажут действительный тип данных для Т. Например, в программе можно задать следующий прототип: double f(int a, double b); //Если это обычная функция, мы //должны обеспечить ее реализацию, //т.к. f() — шаблонная функция, //компилятор реализует код функции, //заменив Т в данном случае на // double. ПРИМЕР 4.10: … как с помощью шаблонов можно уменьшить размер и сложность программ, предоставив компилятору реализацию обобщенных функций. (шаблоны функций min и max) // minmax.h -- Шаблоны функций min и max #ifndef MINMAX_H #define MINMAX_H 1 // Предотвращение повторных include //Т заявлен неопределенным типом, // объект которого возвращает //шаб¬лонная функция mах() template<class T> T max(T a, T b) { //схема для реальных операторов, которые //будут сгенерированы позже if (a > b) return a; return b;} template<class T> T min(T a, T b) { if (a < b) return a; return b;} #endif // _MINMAX_H Рассмотрим подробнее строку 6. В ней Т заявлен неопределенным типом, объект которого возвращает шаблонная функция mахО. Кроме того, функции mах() необходимы два аргумента типа Т. Операторы в функции, приведенные в строках 8-11 — схема для реальных операторов, которые будут сгенерированы позже, когда будет задан действительный тип Т. Шаблон функции min() объявляется аналогичным образом. В программе, использующей шаблонные функции, необходимо лишь указать их прототипы, которые компилятор использует для создания действительных тел функции. В листинге 4.11 приводится пример такой программы. //4_11.cpp //использование шаблонов функций) #include <iostream.h> #include <conio.h> #include "minmax.h" int max(int a, int b); // три прототипа шаблонной //перегруженной ф-ции mах() double max(double a, double b); char max(char a, char b); main() {int i1 = 100, i2 = 200; double d1 = 3.14159, d2 = 9.87654; char c1 = 'A', c2 = 'z'; cout << "max(i1,i2) =="<< max(i1, i2) << '\n'; cout << "max(d1,d2) =="<< max(d1, d2)<<'\n'; cout << "max(c1,c2) =="<< max(c1,c2) << '\n'; //Все функции mах() возвращают //объекты различных типов данных и //требуют два аргумента этих же //типов. getch(); return 0; } В строках 4-6 объявляются три прототипа шаблонной функции mах(). Эти строки дают компилятору необходимую информацию для реализации трех перегруженных функций mахО. Все функции mахО возвращают объекты различных типов данных и требуют два аргумента этих же типов. В основной программе функции mахО вызываются в строках 14-16. Вы можете пользоваться шаблонными функциями точно так же, как и обычными.
Можно использовать более одного неопределенного типа. Для объявления mах() с двумя различными неопределенными типами можно написать: template<class T1, class T2> T1 max(T1 a, T2 b) //функция mах() будет возвращать \\значение типа Т1, и ей необходимы \\два аргумента: один -1 типа Т1, \\другой — типа Т2.
Шаблонные классы Шаблонные классы предоставляют еще большие возможности, чем шаблонные функции. Шаблон класса обеспечивает скелет обобщенного класса для его последующей реализации. Как и шаблоны функций, шаблоны классов чаще всего объявляются в заголовочных файлах. Объявление шаблона класса имеет следующий общий вид: template<class T> class TDatabase { // Закрытые, защищенные и //открытые члены класса } где Т - неопределенный тип, задаваемый пользователем шаблона. (Можно заменить Т на любой другой идентификатор, если захотите.) Т можно впоследствии заменить любым встроенным типом, другим классом, указателем и т.д. TDatabase — имя шаблонного класса. Для ясности заголовок шаблона лучше объявлять в отдельных строках: template<class T> class TAClass { . } Можно также указать несколько неопределенных типов: template<class T1, class T2, class T3> class TAnotherClass { }
ПРИМЕР:4.12: демонстрация объявления шаблона класса, реализующего базу данных с небольшим числом записей.
Листинг 4.12. DB.H (шаблон класса TDatabase) В шаблоне класса класс Т можно использовать для объявления данных-членов, типов возвращаемых функциями-членами значений, параметров и прочих элементов неопределенных типов.
// db.h -- Объявление шаблонного класса TDatabase //#ifndef __DB_H //#define __DB_H 1 // Предотвращение нескольких #include template <class T> class TDatabase { private: T *rp; //указ-тель на записи int num; //число записей public: TDatabase(int n) //\\выделяет память для массива //объектов Т { rp = new T[num = n]; } ~TDatabase() { delete[] rp; } void DoNot(void); T &GetRecord(int recnum); }; template<class T> void TDatabase<T>::DoNot(void) {} template<class T> // возвращает ссылку на объект типа //Т, идентифицируемый номером //записи recnum. T &TDatabase<T>::GetRecord(int recnum) { T *crp = rp; // Указатель на текущую запись = указатель на записи if (0 <= recnum && recnum < num) while (recnum-- > 0) crp++; return *crp; } //#endif //__DB_H
Например, в объявлении класса TDatabase в строке 9 объявляется указатель на Т с именем гр: T *rp; На этой стадии настоящая природа Т еще не известна, поэтому в программе его можно использовать только в самых общих случаях. конструктор TDatabase в строках 12-13 выделяет память для массива объектов Т, присваивая адрес массива указателю гр и заодно устанавливая член num равным требуемому числу записей. (Для простоты в классе игнорируются все связанные с выделением памяти ошибки, которые могут возникнуть.) В строке 15 этот массив удаляется с помощью оператора специального вида delete[] с целью обеспечения вызова деструктора для массива, содержащего объекты класса. В строке 17 объявляется функция-член GetRecordO, возвращающая ссылку на объект типа Т, идентифицируемый номером записи recnum. Это еще один вид обобщенной операции, не требующей знания определенного типа Т. Функции-члены шаблонного класса могут реализоваться встраиваемыми, как это показано на примере конструктора и деструктора класса TDatabase. Или же они могут реализоваться отдельно. Конечно, поскольку эти функции-члены шаблонного класса пока остаются только объявлениями, их вполне можно поместить в заголовочный файл, а не в отдельный модуль. Функция DoNotO, которая, как подсказывает ее имя, не выполняет никаких действий, демонстрирует общий вид реализации функций-членов шаблонного класса: template<class T> void TDatabase<T>::DoNot(void) { Заголовок функции-члена предварен фразой template<class T>. Затем следует тип возвращаемого функци-1 ей значения (void), имя класса (TDatabase<T>) и оператор разрешения области видимости (::). Последним! идет объявление самой функции (DoNothing(void)) и тело (пустое, в данном случае). Используйте этот пример как образец для написания ваших собственных функций-членов шаблонных классов. В строках 25-33 демонстрируется более сложный пример функции-члена шаблонного класса вместе с опера-1 торами, реализующими действия функции. Функция GetRecordO возвращает ссылку на объект типа Т. Внутри функции указатель сгр типа Т присваивается указателю того же типа с именем гр. Иными словами, указатель сгр ссылаетсяна первую запись, сохраненную в объекте класса TDatabase. В строке 29 проверяется, соответствует ли параметр recnum допустимому диапазону значений. Если да, цикл while в строках 30-31 декрементирует параметр recnum до нуля и одновременно передвигает указатель сгр на одну запись в базе данных. Обратите внимание на строку 31. Инкрементирование указателя р с помощью выражения р++ сдвигает I указатель на число байтов объекта, на который ссылается р. В классе Database даже несмотря на то, что тип объекта, на который ссылается сгр, не известен, вполне приемлемо выражение сгр++. Позже, когда будет задан действительный тип для шаблона класса, компилятор сможет сгенерировать соответствующие инструкции для увеличения указателя сгр на величину sizeof(T). Без шаблонных классов подобные обобщенные операторы было бы не так-то легко написать. В строке 32 возвращается разыменованное значение указателя сгр — другими словами, ссылка на любой] объект, адресуемый сгр. Это завершает объявление шаблона класса, в котором не делается никаких предположений о том, какого типа данные в нем запоминаются. Следующий шаг — использование шаблона для создания объекта класса базы данных, способного запомнить некоторое число записей. В листинге 4.13 демонстрируется работа шаблона класса TDatabase.
Листинг 4.13. CTEMPLAT.CPP (использование шаблонов класса) //4_13.cpp #include <conio.h> #include <iostream.h> #include <string.h> #include "db.h" class TRecord { private: char name[41]; public: TRecord () { name[0] = 0; } TRecord(const char *s) { Assign(s); } void Assign(const char *s) { strncpy(name, s, 40); } char *GetName(void) { return name; } };//class int main() { int rn; //индекс числа записей //БД из трех Trecord определяется объект с именем db шаблонного класса типа TDatabase и задается TRecord в качестве класса, замещающего Т в шаблоне. Выражение в скобках (3) — инициализатор db, передаваемый конструктору класса TDatabase. TDatabase<TRecord> db(3); //БД из трх указателей объявляется объект dbp класса TDatabase, состоящий из трех указателей на TRecord TDatabase<TRecord*> dbp(3); //указатель на БД TDatabase<TRecord> *pdb; //указатель на БД указаелей TDatabase<TRecord*> *ppdb; cout<< "\n\nDatabase of 3 TRecords\n"; //вызывается функция GetRecord(O) //для доступа к записи с индексом 0 // Затем в строке вызывается ф-ция- //член Assign() этого объекта для //задания строкового значения db.GetRecord(0).Assign("GeorgeWashington"); db.GetRecord(1).Assign("John Adams"); db.GetRecord(2).Assign("Thomas Jefferson"); for(rn=0; rn<=2;rn++) {cout << db.GetRecord(rn).GetName() << '\n';} cout << "\n\nDatabase of 3 TRecord pointers\n"; dbp.GetRecord(0) = new TRecord("George Bush"); dbp.GetRecord(1) = new TRecord("Ronald Reagan"); dbp.GetRecord(2) = new TRecord("Jimmy Carter"); for (rn = 0; rn <= 2; rn++) cout << dbp.GetRecord(rn)->GetName() << '\n'; cout << "\n\nPointer to database of 3 TRecords\n"; pdb = new TDatabase<TRecord>(3); pdb->GetRecord(0).Assign("John Adams"); pdb->GetRecord(1).Assign("Thomas Jefferson"); pdb->GetRecord(2).Assign("Aaron Burr"); for (rn = 0; rn <= 2; rn++) cout << pdb->GetRecord(rn).GetName() << '\n'; cout << "\n\nPointer to database of 3 TRecord pointers\n"; ppdb = new TDatabase<TRecord *>(3); ppdb->GetRecord(0) = new TRecord("Dan Quayle"); ppdb->GetRecord(1) = new TRecord("George Bush"); ppdb->GetRecord(2) = new TRecord("Walter Mondale"); for (rn = 0; rn <= 2; rn++) cout << ppdb->GetRecord(rn)->GetName() << '\n'; getch(); return 0; } После включения заголовочного файла DB.H (строка 3), содержащего объявление шаблонного класса TDatabase, в программе объявляется класс TRecord — один из объектов для запоминания в TDatabase. Класс TRecord очень прост, в нем объявлен в качестве данных-членов только символьный массив name. Однако TRecord на самом деле может быть произвольным классом, так как в классе TDatabase не делается никаких предположений о типе сохраняемых объектов. В строках 22—25 демонстрируются четыре способа создания объектов класса с помощью шаблона класса. В строке TDatabase<TRecord> db(3); определяется объект с именем db шаблонного класса типа TDatabase и задается TRecord в качестве класса, замещающего Т в шаблоне. Выражение в скобках (3) — инициализатор db, передаваемый конструктору класса TDatabase. Вы можете также создать базу данных из других значений, для этого не обязательно использовать классы. Например, вы можете определить базу данных из 100 вещественных значений двойной точности следующим образом: TDatabase<double> dbd(100); Поскольку класс TDatabase написан для сохранения объектов произвольных типов, он называется контейнерным классом. Обычно, наилучшие контейнерные классы являются полностью обобщенными, хотя этот идеал часто трудно достичь. В строках 23-25 демонстрируется создание других экземпляров шаблонного класса. В строке 23 объявляется объект dbp класса TDatabase, состоящий из трех указателей на TRecord, в строке 24 — указатель pdb на объект класса TDatabase с незаданным числом объектов типа TRecord. Эти примеры могут покрыть большую часть объектов, которые вы можете захотеть создать на основе других шаблонов класса. В программе объект класса TDatabase используется так же, как и любой другой не шаблонный объект. Например, в строке 28 db.GetRecord(0).Assign("George Washington"); вызывается функция GetRecord(O) для доступа к записи с индексом 0. Затем в строке вызывается функция-член Assign() этого объекта для задания строкового значения. В строках 34-55 демонстрируется использование других объектов класса TDatabase. Особенно любопытна строка 35: dbp.GetRecord(O) = new TRecorcK"George Bush"); Поскольку функция GetRecordO возвращает ссылку на объект, она может использоваться в левой части оператора присваивания. В данном случае создается новый объект TRecord, а его адрес присваивается в базе данных записи с индексом нуль, ссылку на которую возвращает функция GetRecordO. Обратите внимание, что во всех этих примерах вам не надо информировать компилятор с помощью приведения типов о типах данных, используемых классом TDatabase. Несмотря на обобщенную природу шаблонов, можно избежать приведения типов и, тем не менее, создавать классы, которые могут поддерживать множество различных типов данных. Вероятно, лучше не писать шаблонные классы с чистого листа. Вместо этого вы можете написать класс TDatabase, использующий заданные объекты TRecord. Затем, после отладки TDatabase, вы можете преобразовать его в универсальный шаблон. Преобразование обычных классов в шаблоны подобным способом помогает также определить истинно обобщенные свойства класса — требование, которое часто является наиболее сложным аспектом создания полезных шаблонов.
Дата добавления: 2014-01-11; Просмотров: 497; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |