前言:在c++的学习中,模板是一个非常重要的知识点,虽然自己编程时很少去写的函数模板,类模板,但是在以后工作的时候,要经常去使用阅读别人写好的模板,而且要是以后要去看stl源码的话,理解c++经常使用的类是如何实现的,那对模板这一块的知识必须要有足够的了解。
1.函数模板的语法
在一个函数之前写上 template <typename T> void test1(){ } 但是我发现写成下面这样也没问题 template <class T>//参数T可以不止一个 void test1(){ }2.函数模板编程练习——实现sort排序,swap函数,打印数组
#include <iostream> #include <cstdlib> using namespace std; template<class T> T mySwap(T &a ,T &b){ T temp = a; a = b; b = temp; } //利用选择排序,对int和char进行排序 template<class T> void mySort(T *arr,int len){ for(int i = 0;i < len;i++){ for(int j = i+1;j < len;j++){ if(arr[i] > arr[j]){ mySwap<T>(arr[i],arr[j]); } } } } template<class T> void Print(T *arr,T len){ for(int i = 0;i<len;i++) cout<<arr[i]<<endl; } void test01(){ char arr[] = {1,2,2,3,4,5,7,8,21}; char arr1[] = {'1','2','3','7','0','a','4','g','r'}; int len = sizeof(arr1)/sizeof(char); mySort<char>(arr1,len); Print<char>(arr1,len); } int main(){ test01(); return 0; }//编译器并不能把函数模板处理成能够处理的任何类型的函数 //函数模板通过集体类型产生不同的函数(通过函数模板产生模板函数)。 //所以编译器会对函数模板进行了两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码在进行一次编译。
模板的局限性: 如果代码实现是定义了赋值操作a=b,但是T为数组,或者是没有这种操作数的类型,这种假设就不成立了。 解决方案: 提供独特的具体化实现,如果可以在这种数据类型中重载此运算符,重载此运算符即可,当然,也可以特殊对待,写一个专门的函数来专门实现,样例入下。
#include <iostream> using namespace std; //模板不常写,但是常用,很多时候要去使用别人的模板 class Person { public: string my_Name; int m_Age; Person(string name,int age) { this->my_Name = name; this->m_Age = age; } //重载==运算符 bool operator==(Person &b) const { if(my_Name == b.my_Name && m_Age == b.m_Age){ return true; } else{ return false; } } }; template <class T> bool myCompare(T &a,T &b){ if(a == b){ return true; } else{ return false; } } //专门为他重新写一个函数 template<>bool myCompare<Person>(Person &a,Person &b){ if(a.my_Name == b.my_Name && a.m_Age == b.m_Age){ return true; } else return false; } int main(){ return 0; }//普通函数能进行自动类型转化,函数模板不可以。 例如下面这个函数
template<class T> T mySwap(T &a ,T &b){ T temp = a; a = b; b = temp; } 如果传进去的是mySwap(int型,char型) 普通函数会进行自动类型转换,是是这个swap函数执行成功,而使用函数模板的话,编译器无法自动判断这个是哪一种类型, 因为两种类型不一样。 注:mySwap<int>(1,2) 和 mySwap(1,2)最后在函数模板中是一样的,编译器会自动判断1,2是int型数据,但是如果是类模板,就必须用第一种。//普通函数可以和模板函数进行重载,优先调用普通函数,如果想要去强制走模板,要传一个空参数列表 //函数模板也可以发生函数重载 //如果函数模板可以产生更好的匹配,那么优先使用函数模板,比如如果要是普通函数要进行自动类型转换,函数模板不需要,那么使用函数模板
1.类模板的基础语法
//template下面紧跟的内容是类,那么这个类就是类模板 #include <iostream> using namespace std; template <class T> class Base1 { public: Base1(){} ~Base1(){} Base1(int){} Base1(const Base1 &){} Base1& operator=(const Base1&){} T& operator[](int){} private: T* m_elements; int m_size; }; int main(){ return 0; }类模板编程练习
#include <iostream> using namespace std; //泛型编程思想 将类型参数化 //类模板成员函数的创建时机,创建出来两个类 // template<class NAMETYPE,class AGETYPE = int//可以有默认值,定义的时候可以不用写 class Person { public: NAMETYPE name; AGETYPE age; Person(NAMETYPE name,AGETYPE age){ this->age = age; this->name = name; } }; int main(){ return 0; }2.类模板与函数模板的异同 不同处 //类模板可以有默认值 //类模板只能使用显示类型推导,不能用隐式 //类模板的类型 也可以有默认参数
相同处 同样每次要编译两次,下面这个代码可以证明
#include <iostream> using namespace std; class Person1 { public: void showPerson1() { cout<< "Person 1 show"<<endl; } }; class Person2 { public: void showPerson2() { cout<< "Person 2 show"<<endl; } }; template<class T> class myClass { public: T obj; void fuc1() { obj.showPerson1(); } //语法通过,他还不清楚传进去的参数是否可以有这些函数 //因为要编译两次 void fuc2() { obj.showPerson2(); } }; void test01(){ myClass<Person1>a; a.fuc1(); /* a.fuc2();//这里其实是不能实现的,因为里面a根本没这个函数 */ } int main(){ test01(); return 0; }3.类模板当做参数—三种实现方法,注释里面也有解释
#include <bits/stdc++.h> using namespace std; //类模板当做参数的三种传入方法,常用第一种,比较简单,另外两个过于复杂,需要配合函数模板 template<class NAMETYPE,class AGETYPE = int> class Person{ public: NAMETYPE nametype; AGETYPE age; Person(NAMETYPE nametype,AGETYPE age){ this->age = age; this->nametype = nametype; } }; //指定穿进去的参数 void doWork(Person<string,int> &a) { cout<<"daSDASDASDASDASDASD"<<endl; } //2参数模板化 template<class T1,class T2> void doWork1(Person<T1,T2> &a){ cout<<typeid(T1).name()<<endl; cout<<typeid(T2).name()<<endl; cout<<"dasdasdasdasdawsdasd"<<endl; } //3整个类模板化 template<class T> void doWork2(T &a){ cout<<typeid(T).name()<<endl; cout<<"sdasdawsdasd"<<endl; } void test01() { Person<string,int>p1("aaa",10); doWork(p1); doWork1<string,int>(p1); doWork2<Person<string,int>>(p1); } int main(){ test01(); return 0; }