246071-Либерти-Освой-самостоятельно-С-за-21-день (852741), страница 96
Текст из файла (страница 96)
Сейчас мы выясняем, что нужно для создания вашегособственного класса, только для того, чтобы вы до конца поняли, как работают шаблоны; но вкоммерческойпрограммевыпочтистопроцентнобудетеиспользоватьклассыбиблиотекиSTL,анесобственногоизготовления.СозданиеэкземплярашаблонаЭкземпляризация (instantiation) — это операция создания определенного типа из шаблона.Отдельныеклассыназываютсяэкземплярамишаблона.Параметризованные шаблоны (parameterized templates) предоставляют возможностьсоздания общего класса и для построения конкретных экземпляров передают этому классу вкачествепараметровтипыданных.ОбъявлениешаблонаОбъявляем параметризованный объект Array (шаблон для массива) путем записиследующихстрок:1:template<classT>//объявляемшаблонипараметр2:classArray//параметризуемыйкласс3:{4:public:5:Array();6://здесьдолжнобытьполноеопределениекласса7:};Ключевое слово template используется в начале каждого объявления и определения классашаблона.
Параметры шаблона располагаются за ключевым словом template. Параметры — этоэлементы, которые изменяются с каждым экземпляром. Например, в приведенном вышешаблоне массивов будет изменяться тип объектов, сохраняемых в массиве. Один экземпляршаблонаможетхранитьмассивцелыхчисел,адругой—массивобъектовклассаAnimals.В этом примере используется ключевое слово class, за которым следует идентификатор Т.Это ключевое слово означает, что параметром является тип. Идентификатор T используется востальной части определения шаблона, указывая тем самым на параметризованный тип. Водном экземпляре этого класса вместо идентификатора T повсюду будет стоять тип int, а вдругом—типCat.ЧтобыобъявитьэкземплярыпараметризованногоклассаArrayдлятиповintиCat,следуетнаписать:Array<int>anIntArray;Array<Cat>aCatArray;Объект anIntArray представляет собой массив целых чисел, а объект aCatArray — массивэлементовтипаCat.ТеперьвыможетеиспользоватьтипArray<int>влюбомместе,гдеобычноуказываетсякакой-либотип—длявозвращаемогофункциейзначения,дляпараметрафункцииит.д.Влистинге19.1содержитсяполноеобъявлениеужерассмотренногонамишаблонаArray.Примечание:Программавлистинге19.1незавершена!Листинг19.1.ШаблонклассаArray1://Листинг19.1.Шаблонклассамассивов2:#include<iostream.h>3:constintDefaultSize=10;4:5:template<classT>//объявляемшаблонипараметр6:classArray//параметризуемыйкласс7:{8:public:9://конструкторы10:Array(intitsSize=DefaultSize);11:Array(constArray&rhs);12:~Array(){delete[]pType;}13:14://операторы15:Array&operator=(constArray&);16:T&operator[](intoffSet){returnpType[offSet];}17:18://методыдоступа19:intgetSize(){returnitsSize;}20:21:private:22:T*pType;23:intitsSize;24:};Результат:Результатовнет.Этапрограмманезавершена.Анализ: Определение шаблона начинается в строке 5 с ключевого слова templateза которым следует параметр.
В данном случае параметр идентифицируется как тип за счетиспользования ключевого слова class, а идентификатор T используется для представленияпараметризованноготипа.Со строки 6 и до конца определения шаблона (строка 24) вся остальная часть объявленияаналогичналюбомудругомуобъявлениюкласса.Единственноеотличиезаключаетсявтом,чтовезде,гдеобычнодолженстоятьтипобъекта,используетсяидентификаторT.Например,можнопредположить,чтоoperator[]долженвозвращатьссылкунаобъектвмассиве,анасамомделеонобъявляетсядлявозвратассылкинаидентификатортипаT.Еслиобъявленэкземплярцелочисленногомассива,перегруженныйоператорприсваиванияэтого класса возвратит ссылку на тип integer. А при объявлении экземпляра массива AnimalоператорприсваиваниявозвратитссылкунаобъекттипаAnimal.ИспользованиеименишаблонаВнутриобъявленияклассасловоArrayможетиспользоватьсябезспецификаторов.Вдругомместе программы этот класс будет упоминаться как Array<T>.
Например, если не поместитьконструкторвнутриобъявлениякласса,товыдолжнызаписатьследующее:template<classT>Array<T>::Array(intsize):itsSize=size{pType=newT[size];for(inti=0;i<size;i++)pType[i]=0;}Объявление, занимающее первую строку этого фрагмента кода, устанавливает в качествепараметра тип данных (class T). В таком случае в программе на шаблон можно ссылаться какArray<T>,аобъявленнуюфункцию-членвызыватьстрокойArray(intsize).Остальнаячастьфункцииимееттакойжевид,какоймогбытьулюбойдругойфункции.Этообычныйипредпочтительныйметодсозданияклассаиегофункцийпутемпростогообъявлениядовключениявшаблон.ВыполнениешаблонаДля выполнения класса шаблона Array необходимо создать конструктор-копировщик,перегрузить оператор присваивания (operator=) и т.д. В листинге 19.2 показана простаяконсольнаяпрограмма,предназначеннаядлявыполненияэтогошаблона.Примечание:Некоторые более старые компиляторы не поддерживают использованиешаблонов.
Но шаблоны являются частью стандарта ANSI C++, поэтому компиляторы всехосновных производителей поддерживают шаблоны в своих текущих версиях. Если у вас оченьстарый компилятор, вы не сможете компилировать и выполнять упражнения, приведенные вэтой главе. Однако все же стоит прочитать ее до конца, а затем вернуться к этомуматериалупослемодернизациисвоегокомпилятора.Листинг19.2.Использваниешаблонамассива1:#include<iostream.h>2:3:constintDefaultSize=10;4:5://обычныйклассAnimalдля6://созданиямассиваживотных7:8:classAnimal9:{10:public:11:Animal(int);12:Animal();13:~Animal(){}14:intGetWeight()const{returnitsWeight;}15:voidDisplay()const{cout<<itsWeight;}16:private:17:intitsWeight;18:};19:20:Animal::Animal(intweight):21:itsWeight(weight)22:{}23:24:Animal::Animal():25:itsWeight(0)26:{}27:28:29:template<classT>//обьявляемшаблонипараметр30:classArray//параметризованныйкласс31:{32:public:33://конструкторы34:Array(intitsSize-DefaultSize);35:Array(constArray&rhs);36:~Array(){delete[]pType;}37:38://операторы39:Array&operator=(constArray&);40:T&operator[](intoffSet){returnpType[offSet];}41:constT&operator[](intoffSet)const42:{returnpType[offSet];}43://методыдоступа44:intGetSize()const{returnitsSize;}45:46:private:47:T*рТуре;48:intitsSize;49:};50:51://выполнения...52:53://выполняемконструктор54:template<classT>55:Array<T>::Array(intsize):56:itsSize(size)57:{58:pType=newT[size];59:for(inti=0;i<size;i++)60:pType[i]=0;61:}62:63://конструктор-копировщик64:template<classT>65:Array<T>::Array(constArray&rhs)66:{67:itsSize=rhs.GetSize();68:pType=newT[itsSize];69:for(inti=0;i<itsSize;i++)70:pType[i]=rhs[i];71:}72:73://операторприсваивания74:template<classT>75:Array<T>&Array<T>::operator=(constArray&rhs)76:{77:if(this==&rhs)78:return*this;79:delete[]pType;80:itsSize=rhs.GetSize();81:pType=newT[itsSize];82:for(inti=0;i<itsSize:i++)83:pType[i]=rhs[i];84:return*this;85:}86:87://исполняемаяпрограмма88:intmain()89:{90:Array<int>theArray;//массивцелых91:Array<Animal>theZoo;//массивживотных92:Animal*pAnimal;93:94://заполняеммассивы95:for(inti=0;i<theArray.GetSize();i++)96:{97:theArray[i]=i*2;98:pAnimal=newAnimal(i*3);99:theZoo[i]=*pAnimal;100:deletepAnimal;101:}102://выводимнапечатьсодержимоемассивов103:for(intj=0;j<theArray.GetSize();j++)104:{105:cout<<"theArray["<<j<<"]:\t";106:cout<<theArray[j]<<"\t\t";107:cout<<"theZoo["<<j<<"]:\t";108:theZoo[j].Display();109:cout<<endl;110:}111:112:return0;113:}Результат:theArray[0]0theZoo[0]0theArray[1]2theZoo[1]3theArray[2]4theZoo[2]-6theArray[3]6theZoo[3]9theArray[4]8theZoo[4]12theArray[5]10theZoo[5]15theArray[6]12theZoo[6]18theArray[7]14theZoo[7]21theArray[8]16theZoo[8]24theArray[9]18theZoo[9]27Анализ:Встроках8-26выполняетсясозданиеклассаAnimal,благодарякоторомуобъектыопределяемогопользователемтипаможнобудетдобавлятьвмассив.Содержимое строки 29 означает, что в следующих за ней строках объявляется шаблон,параметромдлякоторогоявляетсятип,обозначенныйидентификаторомТ.КлассArrayсодержитдваконструктора,причемпервыйконструкторпринимаетразмерипоумолчаниюустанавливаетегоравнымзначениюцелочисленнойконстантыDefaultSize.Затем объявляются операторы присваивания и индексирования, причем объявляютсяконстантная и не константная версии оператора индексирования.
В качестве единственногометодадоступаслужитфункцияGetSize(),котораявозвращаетразмермассива.Можно, конечно, представить себе и более полный интерфейс. Ведь для любой серьезнойпрограммы создания массива представленный здесь вариант будет недостаточным. Какминимум, пришлось бы добавить операторы, предназначенные для удаления элементов, дляраспаковкииупаковкимассиваит.д.ВсеэтопредусмотреноклассамиконтейнеровбиблиотекиSTL,нокэтомумывернемсявконцезанятия.Раздел закрытых данных содержит переменные-члены размера массива и указатель намассивобъектов,реальнопомещенныхвпамять.ФункциишаблонаЕсливыхотитепередатьфункцииобъектмассива,нужнопередаватьконкретныйэкземплярмассива, а не шаблон. Поэтому, если некоторая функция SomeFunction() принимает в качествепараметрацелочисленныймассив,используйтеследующуюзапись:voidSomeFunction(Array<int>&);//правильноАзаписьvoidSomeFunction(Array<T>&);//ошибка!неверна,посколькуотсюданеясно,чтопредставляетсобойвыражениеT&.ЗаписьvoidSomeFunction(Array&);//ошибка!тоже ошибочна, так как объекта класса Array не существует — есть только шаблон и егоэкземпляры.Чтобы реализовать более общий подход использования объектов, созданных на основешаблона,нужнообъявитьфункциюшаблона:template<classT>voidMyTemplateFunction(Array<T>&);//верноЗдесьMyTemplateFunction()объявленакакфункцияшаблона,начтоуказываетперваястрокаобъявления.
Заметьте, что функции шаблонов, подобно другим функциям, могут иметь любыеимена.Функциишаблонов,помимообъектов,заданныхвпараметризованнойформе,могуттакжеприниматьиэкземплярышаблона.Проиллюстрируемэтонапримере:template<classT>voidMyOtherFunction(Array<T>&,Array<int>&);//верноОбратите внимание на то, что эта функция принимает два массива: параметризованныймассивимассивцелыхчисел.Первыйможетбытьмассивомлюбыхобъектов,авторой—толькомассивомцелыхчисел.ШаблоныидрузьяВшаблонахклассовмогутбытьобъявленытритипадрузей:•дружественныйклассилифункция,неявляющиесяшаблоном;•дружественныйшаблонклассаилифункция,входящаявшаблон;• дружественный шаблон класса или шаблонная функция, специализированные по типуданных.Дружественныеклассыифункции,неявляющиесяшаблонамиМожнообъявитьлюбойклассилифункцию,которыебудутдружественныпоотношениюквашемуклассушаблона.Вэтомслучаекаждыйэкземпляркласса:будетобращатьсясдругомтак,какбудтообъявлениекласса-другабылосделановэтомконкретномэкземпляре.Влистинге19.3вопределениишаблонаклассаArrayдобавленатривиальнаядружественнаяфункцияIntrude(),ав управляющей.программе делается вызов этой функции.
В качестве друга функция Intrude()получает доступ к закрытым данным класса Array. Но поскольку эта функция не являетсяфункциейшаблона,тоееможновызыватьтолькодлямассивазаданноготипа(внашемпримередлямассивацелыхчисел).Листинг18.3.Функция-друг,неявляющаясяшаблоном1://Листинг19.3.Использованиевшаблонахфункций-друзейопределенноготипа2:3:#include<iostream.h>4:5:constintDefaultSize=10;6:7://обьявляемпростойклассAnimal,чтобыможно8://былосоздатьмассивживотных9:10:classAnimal11:{12:public:13:Animal(int);14:Animal();15:~Animal(){}16:intGetWeight()const{returnitsWeight;}17:voidDisplay()const{cout<<itsWeight;>18:private:19:intitsWeight;20:};21:22:Animal::Animal(intweight):23:itsWeight(weight)24:{}25:26:Animal::Animal():27:itsWeight(0)28:{}29:30:template<classT>//обьявляемшаблонипараметр31:classArray//параметризованныйкласс32:{33:public:34://конструкторы35:Array(intitsSize=DefaultSize);36:Array(constArray&rhs);37:~Array(){delete[]pType;}38:39://операторы40:Array&operator=(constArray&);41;T&operator[](intoffSet){returnpType[offSet];}42:constT&operator[](intoffSet)const43:{returnpType[offSet];}44://методыдоступа45:intGetSize()const{returnitsSize;}46:47://функция-друг48:friendvoidIntrude(Array<int>);4950:private:51:T*рТуре;52:intitsSize;53:};54:55://Посколькуфункция-другнеявляетсяшаблоном,ееможноиспользоватьтолько56://смассивамицелыхчисел!Ноонаполучаетдоступкзакрытымданнымкласса.57:voidIntrude(Array<int>theArray)58:{59:cout<<"\n***Intrude***\n";60:for(inti=0;i<theArray.itsSize;i++)61:cout<<"i:"<<theArray.pType[i]<<endl;62:cout<<"\n"63:}64:65://Рядвыполнений...66:67://выполнениеконструктора68:template<classT>69:Array<T>::Array(intsize):70:itsSize(size)71:{72:pType=newT[size];73:for(inti=0;i<size;i++)74:pType[i]=0;75:}76:77://конструктор-копировщик78:template<classT>79:Array<T>::Array(constArray&rhs)80:{81:itsSize=rhs.GetSize();82:pType=newT[itsSize];83:for(inti=0;i<itsSize;i++)84:pType[i]=rhs[i];85:}86:87://перегрузкаоператораприсваивания(=)88:template<classT>89:Array<T>&Array<T>::operator=(constArray&rhs)90:{91:if(this==&rhs)92:return*this;93:delete[]pType;94:itsSize=rhs.GetSize();95:pType=newT[itsSize];96:for(inti=0;i<itsSize;i++)97:pType[i]=rhs[i];98:return*this;99:}100:101://управляющаяпрограмма102:intmain()103:{104:Array<int>theArray;//массивцелых105:Array<Animal>theZoo;//массивживотных106:Animal*pAnimal;107:108://заполняеммассивы109:for(inti=0;i<theArray.GetSize();i++)110:{111:theArray[i]=i*2;112:pAnimal=newAnimal(i*3);113:theZoo[i]=*pAnimal;114:}115:116:intj;117:for(j=0;j<theArray.GetSize();j++)118:{119:cout<<"theZoo["<<j<<"]:\t";120:theZoo[j].Display();121:cout<<endl;122:}123:cout<<"Nowusethefriendfunctionto";124:cout<<"findthemembersofArray<int>";125:Intrude(theArray);126:127:cout<<"\n\nDone.\n";128:return0;129:}Результат:theZoo[0]:0theZoo[1]:3theZoo[2]:6theZoo[3]:9theZoo[4]:12theZoo[5]:15theZoo[6]:18theZoo[7]:21theZoo[8]:24theZoo[9]:27NowusethefriendfunctiontofindthemembersofArray<int>***Intrude***i:0i:2i:4i:6i:8i:10i:12i:14i:16i:18Done.Анализ: Объявление шаблона Array было расширено за счет включения дружественнойфункции Intrude().