第06章模板泛型编程基础-2-本章内容安排什么是模板函数模板类模板通用数组类UniversalArray雇员管理系统版本6(基于模板)-3-引入问题问题:多个函数的参数个数相同,处理代码雷同,但处理的数据类型不同解决方案一:为不同函数设置不同的名字intimax(inta,intb){returnab?a:b;}longlmax(longa,longb){returnab?a:b;}doubledmax(doublea,doubleb){returnab?a:b;}存在多个函数版本,代码重复,函数命名复杂混乱,客户代码难以使用。-4-引入问题解决方案二:函数重载intmax(inta,intb){returnab?a:b;}longmax(longa,longb){returnab?a:b;}doublemax(doublea,doubleb){returnab?a:b;}解决函数命名问题,多个函数有统一命名,但代码冗余,存在多个雷同的实现,维护的代价高。-5-解决之道:模板技术模板技术:将函数参数类型提取出来作为“类型参数”,通过创建通用的函数模板,避免函数体的重复定义。模板技术适用与类模板,将类中所处理的数据类型提取出来作为“类型参数”,创建通用的类模板。-6-本章内容安排什么是模板函数模板类模板模板应用实例通用数组类UniversalArray雇员管理系统版本6(基于模板)-7-定义函数模板templatetypenameTT&max(Tconst&a,Tconst&b){returnab?b:a;}max定义了一个模板(并不是一个函数),描述了函数的骨架,比较2个值并返回大者,两个值通过a和b传递,而参数类型还没确定,用模板参数T来代替。标志模板的开始,同时指定模板中使用的类型参数-8-调用函数:函数模板实例化#includeiostreamintmain(){std::coutmax(3,6)std::endl;charc1=‘a’;charc2=‘b’;std::coutmax(c1,c2)std::endl;return0;}调用max函数,编译器根据实参类型和函数模板的骨架,生成针对不同参数类型的max函数版本(函数实例)。实际生成maxint和maxchar2个版本函数,如果客户程序中不调用max,则可能不生成任何函数实例。-9-函数模板与模板函数在编译阶段,编译器根据函数调用时实参的类型以及函数模板,生成实际的函数定义,再进行编译。函数模板:通用的函数模板,支持类型参数化,是产生实际函数定义的模板。模板函数:调用函数时根据实参的类型,编译器生成函数实例并对其进行编译。-10-编译器生成的函数实例intmaxint(intconst&a,intconst&b){returnab?b:a;}charmaxchar(charconst&a,charconst&b){returnab?b:a;}通过模板技术,我们只需要维护1份代码骨架,由编译器编译时替我们生成代码,解决了代码冗余、难以维护等问题。-11-模板参数的演绎templatetypenameTT&max(Tconst&a,Tconst&b){returnab?b:a;}max(4,7);//编译器能够推断所有的T均为int型max(4,4.2);当编译器无法推断类型参数时,就会出现二义性,从而报错。4是int型,4.2是double类型,无法决定T的类型。X-12-显式指定模板参数类型templatetypenameTT&max(Tconst&a,Tconst&b){returnab?b:a;}maxint(4,7);maxdouble(4,4.2);在可能出现二义性的情况下,显式指定模板那参数类型,是一种良好编程习惯。-13-本章内容安排什么是模板函数模板类模板通用数组类UniversalArray雇员管理系统版本6(基于模板)-14-类模板的定义templatetypenameTclassExam{public:voidsetValue(Tconst&value);TgetValue()const;private:Telems;};Exam并不是一个真正的类,只是一个类模板(生成类定义的骨架),类中数据成员和部分成员函数参数的类型还没有确定,并以模板并没有定义任何类。标志模板的开始,同时指定模板中使用的类型参数-15-类模板的定义templatetypenameTvoidExamT::setValue(constT&value){elems=vlaue;}templatetypenameTTExamT::getValue()const{returnelems;}类作用域限定符不再是Exam,Exam不是真正的类,真正的类是按照T类型实例化后的类,名称为ExamT。每个成员函数之前都要使用模板标志,哪怕该函数不使用类型参数。-16-定义对象:类模板实例化#includeiostreamintmain(){Examinte1;e1.setValue(5);std::coute1.getValue()std::endl;Examdoublee2;e2.setValue(5.5);std::coute2.getValue()std::endl;return0;}创建对象时,编译器根据指定的类型,生成特定类型的类定义(含成员函数实现)。根据类骨架,编译器针对不同类型帮我们生成类定义。-17-编译器生成的类实例classExamint{public:voidsetVal(intconst&);intgetVal()const;private:intelems;};voidExamint::setVal(constint&e){elems=e;}intExamint::getVal()const{returnelems;}借助模板的强大功能,我们无需编写多个类,编译器根据类型参数帮我们生成实际需要的类。-18-编译器生成的类实例classExamdouble{public:voidsetVal(doubleconst&);doublegetVal()const;private:doubleelems;};voidExamdouble::setVal(constdouble&e){elems=e;}doubleExamdouble::getVal()const{returnelems;}-19-本章内容安排什么是模板函数模板类模板通用数组类UniversalArray雇员管理系统版本6(基于模板)-20-1、不同数组类的尴尬3个不同的数组类Array1D:以int为数据成员,动态内存分配ArrayEmployee:以Employee为数据成员,动态内存分配ArrayEmployee:以Employee*为数据成员,动态内存分配3个数组类,只是处理的数据类型不同,但整个处理逻辑和接口类似,应使用模板技术。-21-2、模板数组类的定义templatetypenameTclassUniversalArray{public:UniversalArray(intmaxSize=1);UniversalArray(constUniversalArrayT&a);UniversalArray&operator=(constUniversalArrayT&a);~UniversalArray();intgetSize()const{returnsize;}TgetValue(intindex)const;voidsetValue(intindex,Tvalue);T&operator[](intindex);constT&operator[](intindex)const;voidpushData(Tdata);voidremoveData();voidremoveData(intindex);此时Array变成了模板,而不是真正的类,编译器将根据模板,帮助我们生成所需要的类。-22-模板数组类的定义templatetypenameTclassUniversalArray{…private:T*pData;intsize;intmaxSize;boolvalidIndex(intindex)const;voidcopyData(T*data,intmaxSize,intcurSize);voidreAllocMemory();};-23-成员函数的实现templatetypenameTvoidUniversalArrayT::copyData(T*data,intmaxSize,intcurSize){inti;this-maxSize=maxSize;size=curSize;pData=newT[maxSize];for(i=0;isize;++i){pData[i]=data[i];}}每个成员函数前的template声明都需要保留。数据元素的类型用T来代替。鉴于模板的特殊机制,要求将成员函数的实现和模板定义都放在头文件中。-24-成员函数的实现templatetypenameTUniversalArrayT::UniversalArray(intmaxSize){this-maxSize=maxSize;pData=newT[maxSize];size=0;}templatetypenameTUniversalArrayT::~UniversalArray(){delete[]pData;}-25-成员函数的实现templatetypenameTUniversalArrayT::UniversalArray(constUniversalArrayT&a){copyData(a.pData,a.maxSize,a.size);}templatetypenameTUniversalArrayT&UniversalArrayT::operator=(constUniversalArrayT&a){if(this==&a)return*this;delete[]pData;copyData(a.pData,a.maxSize,a.size);return*this;}-26-成员函数的实现templatetypenameTboolUniversalArrayT::validIndex(intindex)const{if(index0||index=size)returnfalse;returntrue;}templatetypenameTvoidUniversalArrayT::setValue(intindex,Tvalue){if(validIndex(index)==false)return;pData[index]=value;}-27-成员函数的实现templatetypenameTTUniversalArrayT::getValue(intindex)const{if(validIndex(index)==false){std::couterror:Invalidindex!\n;exit(0);}returnpData[index];}-28-成员函数的实现templatetypenameTT&UniversalArrayT::operator[](intindex){if(validIndex(index)==false){std::couterror:Invalidindex\n;exit(0);}returnpData[index];}templatetypenameTco