一:函数模板
建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。
凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需要在模板中定义一次即可
(一)函数模板语法
template <类型形式参数表>
类型 函数名(形参参数表)
{
执行语句;
}
其中类型形式参数表形式为: typename T1,typename T2,...,typename Tn 或者 class T1,class T2,...,class Tn
(二)函数模板和普通函数相比
(1)参数转换问题
普通函数可以进行隐式函数类型转换
模板函数不允许存在这种转换,类型严格一致
(2)调用规则问题
1.函数模板可以像普通函数一样被重载 2.C++编译器优先考虑普通函数 3.如果函数模板可以产生一个更好的匹配,那么选择模板 4.可以通过空模板实参列表的语法限定编译器只通过模板匹配
(三)函数模板的使用
template <typename T1,typename T2> //参数列表为2个 void sortArray(T1* arr, T2 num) { T2 i, j; T1 temp; for (i = 0; i < num; i++) for (j = 0; j < num; j++) if (arr[i]>arr[j]) { temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } template <typename T> void printArr(T* arr, int num) //参数列表可以含有固定类型参数 返回值类型可以自定义,也可以为typename定义类型 { int i; for (i = 0; i < num; i++) { cout << arr[i]<<" "; } cout << endl; } void main() { //测试int类型 int a[5] = { 1, 37, 20, 19, 49 }; sortArray(a, 5); printArr(a, 5); //测试float类型 float fa[5] = { 2.6, 37.1, 62.9, 16.2, 38 }; sortArray(fa, 5); printArr(fa, 5); system("pause"); }
(四)当函数模板遇到函数重载《重点》
1.函数模板可以像普通函数一样被重载
//实现参数交换 template<typename T> void mySwap(T& t1, T& t2) { T temp; temp = t1; t1 = t2; t2 = temp; } void mySwap(char &b, int& a) { int temp; temp = a; a = b; b = temp; } void main() { char a = 'A'; int b = 97; mySwap(a, b); cout << "a:" << a<<endl; cout << "b:" << b << endl; system("pause"); }
由于模板函数参数类型严格定义,故会去找下面的进行调用。但是不难看出是允许函数模板重载的
2.C++编译器优先考虑普通函数
//实现参数交换 template<typename T> void mySwap(T& t1, T& t2) { T temp; temp = t1; t1 = t2; t2 = temp; cout << "template func" << endl; } void mySwap(int &b, int& a) { int temp; temp = a; a = b; b = temp; cout << "ordinary func" << endl; } void main() { int a = 16, b = 64; mySwap(a, b); cout << "a:" << a<<endl; cout << "b:" << b << endl; system("pause"); }
可以看出模板函数和普通函数都是可以匹配成功的,但是C++编译器却优先选取了普通函数
3.如果函数模板可以产生一个更好的匹配,那么选择模板
//实现参数交换 template<typename T> void mySwap(T& t1, T& t2) { T temp; temp = t1; t1 = t2; t2 = temp; cout << "template func" << endl; } void mySwap(int &b, int& a) { int temp; temp = a; a = b; b = temp; cout << "ordinary func" << endl; } void main() { float a = 16.5, b = 64.6; mySwap(a, b); cout << "a:" << a<<endl; cout << "b:" << b << endl; system("pause"); }
可以看出,在符合模板函数参数列表严格匹配的条件下,若是和模板函数更好匹配,那么会去选择模板函数
4.可以通过空模板实参列表的语法限定编译器只通过模板匹配
template<typename T> void mySwap(T& t1, T& t2) { T temp; temp = t1; t1 = t2; t2 = temp; cout << "template func" << endl; } void mySwap(int &b, int& a) { int temp; temp = a; a = b; b = temp; cout << "ordinary func" << endl; } void main() { int a = 16, b = 64; mySwap(a, b); //若是不做处理会优先调用普通函数 //下面通过<>类型列表,显示调用模板函数 mySwap<>(a, b); cout << "a:" << a<<endl; cout << "b:" << b << endl; system("pause"); }
通过空模板<>列表可以显示调用模板函数
(五)总结
1.编译器并不是把函数模板处理成能够处理任意类的函数 2.编译器从函数模板通过具体类型产生不同的函数 3.编译器会对函数模板进行两次编译: 在声明的地方对函数模板进行编译 在调用的地方对参数替换后的代码进行编译
二:类模板
(一)单个类模板语法
//也可以通过类属参数进行直接传参 template<typename T,int exp1,typename T3> //类属参数列表写了,可以不用(不会报错),但是既然不用,写他也就没有意义 class A { public: T t; //注意:类属参数列表中的参数,至少在类说明中出现一次 public: A(int a) { this->t = a + exp1; } void getInfo() { cout <<"getInfo:"<<this->t << endl; } }; void main() { A<int,10,int> a(5); //模板类调用 a.getInfo(); system("pause"); }
(二)继承中的函数模板:派生类必须实例化基类(指定好参数列表)
template<typename T> class A { public: T t; public: A(T a) { this->t = a ; } void getInfo() { cout <<"A getInfo:"<<this->t << endl; } }; class B :public A<float> { public: int b; public: B(int n, float m) :A<float>(m) { b = n; } void getInfo() { cout << "B getInfo:" << this->b << endl;; A<float>::getInfo(); } }; void main() { B b(5, 6.5); b.getInfo(); system("pause"); }
总之:子类从模板类继承时,一定要让编译器知道父类的具体类型,才可以进行内存分配。
(三) 类模板方法在类外部进行定义(上面都是在内部定义)
template <class 虚拟类型参数>
函数类型 类模板名<虚拟类型参数>::成员函数名(函数参数列表)
{
....
}
template<typename T> class A { public: T t; public: A(T a) { this->t = a ; } T getInfo(); //需要提前声明 }; template<class T> //typename也可以 T A<T>::getInfo() { cout << "A outfunc" << endl; return this->t; } void main() { int ret; A<int> a(5); ret=a.getInfo(); cout << "ret:" << ret << endl; system("pause"); }
(四)类模板友元函数使用(声明,实现,调用3个都要注意)
template<typename T> class A { public: T t; public: A(T a) { this->t = a ; } T getInfo(); friend A<T> mySub(A<T> &a1, A<T> &a2); //1.声明 }; template <typename T> //2.实现 A<T> mySub(A<T> &a1, A<T> &a2) { A<T> a(a1.t - a2.t); return a; } template<typename T> T A<T>::getInfo() { cout << "A outfunc" << endl; return this->t; } void main() { int ret; A<int> a(5); A<int> b(2); //注意友元函数的调用方法 A<int> c = mySub<int>(a, b); //3.调用 //返回匿名对象,直接初始化给c ret=c.getInfo(); cout << "ret:" << ret << endl; system("pause"); }