什么是模板
模板编程也可以叫做泛型编程,忽略数据类型的一种编程方式
//求最值问题 int Max(int a,int b) { return a>b?a:b; } double Max(int a,int b) { return a>b?a:b; } string Max(string a,string b) { return a>b?a:b; } //引入模板编程 template <typename type> //告诉编译器,下面会用到一个未知类型叫做type type Max(type a,type b) { return a>b?a:b; }
模板代码
#include <iostream> using namespace std; template <typename type> type Max(type a, type b) { return a > b ? a : b; } //typename 可以用class 替换 template <class T> void print(T data) { cout << data << endl; } int main() { //隐式调用 cout << Max(1, 2) << endl; cout << Max(1.1, 2.2) << endl; //string 和char* 有区别 cout << Max(string("1ILoveyou"), string("2IMissyou")) << endl; //显示调用 <>传类型的参数 cout << Max<int>(1, 2) << endl; //type=int a=1 b=2 cout << Max<string>(string("1"), string("2")) << endl; cout << Max<double>(1.2, 1.3) << endl; return 0; }
函数模板
函数模板重载问题
- 函数模板和普通函数
- 函数模板和函数模板
#include <iostream> using namespace std; //No.1 模板与普通函数 int Max(int a, int b) { cout << "普通函数..." << endl; return a > b ? a : b; } template <class T> T Max(T a, T b) { cout << "模板" << endl; return a > b ? a : b; } //No.2 模板与模板 template <class type1,class type2,class type3> void print(type1 one, type2 two, type3 three) { cout << "三只" << endl; } template <class type1,class type2> //type1=int type2=double void print(type1 one, type1 two, type2 tow) //int int double { cout << "两只" << endl; } template <class type> void print(type one, type two, type three) { cout << "一只" << endl; } int main() { cout << Max<int>(1, 2) << endl; //显式调用,百分百调用模板 cout << Max(1, 2) << endl; //优先调用类型确定的函数 cout << "显示调用" << endl; print<int, double, string>(1, 1.1, string("23")); print<int, double>(1, 1, 1.22); print<int>(1, 2, 3); cout << "隐式调用" << endl; print(1, 1, 2); //需要传参越少先调用 print(1, 1, string("sdsd")); print(1, 1.11, string("sdsd")); //只有一种选择 return 0; }
类成员函数是函数模板
//这种不叫做模板类型 class MM { public: template <class T> void print(T data) { cout << data << endl; } protected: }; int main() { MM mm; mm.print(1); mm.print<string>("string"); return 0; }
函数模板缺省
函数模板缺省和函数参数的缺省是一样的规则
//函数模板缺省 template <class type1,class type2=string> void printData(type1 one, type2 two) { cout << one << endl; cout << two << endl; } int main() { printData<int, double>(1, 1.22); printData<int>(1, string("dsfsdf")); return 0; }
函数模板传常量
//函数模板传常量 template <class T,size_t size> void printArray(T* array) { for (int i = 0; i < size; i++) { cout << array[i] << " "; } cout << endl; } int main() { int num[3] = { 1,2,3 }; printArray<int, 3>(num); //下面代码报错 //int length = 3; //printArray<int, length>(num); string str[4] = { "sdds","sd","sdsd" ,"sdds"}; printArray<string, 4>(str); return 0; }
类模板
类模板的基础
- 怎么写类模板
- 类模板不是一个完整类型,所以任何用到类名的地方都需要用类名<未知类型>的方式使用
- 怎么去使用类模板,类模板必须采用显式调用方式
- 类模板在多文件中不能分开写
- 可以写在.hpp文件中(声明和实现都在一起)
#include <iostream> #include <map> using namespace std; template <class type1,class type2> struct my_pair { type1 first; //键 type2 second; //值 my_pair(type1 first, type2 second) :first(first), second(second) {} my_pair() = default; }; template <class type1,class type2> my_pair<type1, type2> my_make_pair(type1 one, type2 two) { return my_pair<type1, type2>(one, two); } template <class type1,class type2> class Test { public: Test(type1 one, type2 two) :one(one), two(two) { } void printTest(); protected: type1 one; type2 two; }; template <class type1, class type2> void Test<type1,type2>::printTest() { cout << one << " " << two << endl; } template <class type1,class type2> class Data :public Test<type1,type2> { public: Data(type1 one, type2 two) :Test<type1, type2>(one, two) { } protected: }; int main() { my_pair<int,int> pairData = { 1,2 }; cout << pairData.first << " " << pairData.second << endl; my_pair<int, string>* p = new my_pair<int, string>; p->first = 12; p->second = "sdsd"; cout << p->first << " " << p->second << endl; Data<int, int> data(1, 2); data.printTest(); //标准库中的pair类型 pair<int, string> pD(1, "ILoveyou"); cout << pD.first << " " << pD.second << endl; pair<int, string> testData = make_pair<int, string>(1, "sdfsdf"); my_pair<int, string> my_testData =my_make_pair<int, string>(1, "sdfsdf"); return 0; }
类模板特化问题
- 局部特化
- 完全特化
特化的目的是为了适应不同数据的不同处理
#include <iostream> using namespace std; template <class _Ty1,class _Ty2,class _Ty3> class Data { public: Data(_Ty1 one, _Ty2 two, _Ty3 three) :one(one), two(two), three(three) {} void printData() { cout << (one + two + three) << endl; } private: _Ty1 one; _Ty2 two; _Ty3 three; }; //局部特化 //两个数据,打印两数之差 template <class _Ty1,class _Ty2> class Data<_Ty1,_Ty1,_Ty2> { public: Data(_Ty1 one, _Ty2 two) :one(one), two(two){} void printData() { cout << (one -two) << endl; } private: _Ty1 one; _Ty2 two; }; //只有一个数据,打印数据 template <class _Ty1> class Data<_Ty1,_Ty1,_Ty1> { public: Data(_Ty1 one) :one(one){} void printData() { cout << one << endl; } private: _Ty1 one; }; //完全特化 template <> class Data<string, string, string> { public: Data(string one,string two,string three):one(one),two(two),three(three) { } void printData(); private: string one; string two; string three; }; void Data<string,string,string>::printData() { cout << one << " " << two << " " << three << endl; } int main() { Data<int, int, int> data1(1); data1.printData(); Data<int,int, double> data2(2, 1); data2.printData(); Data<int, double, float> data3(1, 1.1, 1.2f); data3.printData(); Data<string, string, string> data4("dsd","sdfd","sdfdsf"); data4.printData(); return 0; }
模板操作自定义类型
- 模板操作自定义关键点在于重载
#include <iostream> #include <algorithm> using namespace std; class MM { public: MM() = default; MM(string name, int age) :name(name), age(age) {} friend ostream& operator<<(ostream& out, const MM& object) { out << object.name << " " << object.age; return out; } private: string name; int age; }; template <class _Ty> void printData(_Ty data) { cout << data << endl; } template <class _Ty1,class _Ty2,class _Ty3> class Data { public: //_Ty1=MM, _Ty2=int, _Ty3=int Data(_Ty1 one, _Ty2 two, _Ty3 three) :one(one), two(two), three(three) { } void printData() { cout << one << " " << two << " " << three << endl; } private: _Ty1 one; _Ty2 two; _Ty3 three; }; int main() { printData(1); printData("string"); MM mm = { "小芳",18 }; printData(mm); Data<MM, int, int> data(MM("小芳",18),98,99); data.printData(); Data<MM, MM, MM> mmData(MM("小芳", 18), MM("小芳", 18), MM("小芳", 18)); mmData.printData(); return 0; }
模板嵌套模板
- 关键点在于大家自己要清楚类型如何表示(类型是由类名<类型>表示一个类型)
#include <iostream> using namespace std; template <class _Ty1,class _Ty2,class _Ty3> class Data { public: Data(_Ty1 one, _Ty2 two, _Ty3 three) :one(one), two(two), three(three) {} void printData() { cout << one << " " << two << " " << three << endl; } friend ostream& operator<<(ostream& out, Data<_Ty1, _Ty2, _Ty3>& object) { out << object.one << " " << object.two << " " << object.three; return out; } protected: _Ty1 one; _Ty2 two; _Ty3 three; }; template <class _Ty1, class _Ty2> class Info { public: Info(_Ty1 one, _Ty2 two) :one(one), two(two) {} void printData() { cout << one << " " << two << endl; } //template <class _Ty1, class _Ty2> 类中实现不需要修饰了,会出现重定义问题 friend ostream& operator<<(ostream& out, Info<_Ty1, _Ty2>& object) { out << object.one << " " << object.two << " "; return out; } protected: _Ty1 one; _Ty2 two; }; template <class _Ty1> class Student { public: Student(_Ty1 one) :one(one) {} void printData() { cout << one << endl; } protected: _Ty1 one; }; int main() { Data<int, int, int> data(1,1,1); //Data<int, int, int> Info<int, int> info(1, 2); //Info<int, int> Info<Data<int, int, int>, Data<string, string, string>> test1(Data<int, int, int>(1, 1, 1), Data<string, string, string>("ds", "sd", "sds")); //起别名 using type1 = Data<int, int, int>; using type2 = Data<string, string, string>; //别名版本 Info<type1, type2>test2(type1(1, 1, 1), type2("ds", "sd", "sds")); Data<Info<int, string>, Info<string, string>, Info<int, double>> test3(Info<int, string>(1,"sd"), Info<string, string>("sdds","dsds"),Info<int, double>(1,1.11)); test1.printData(); test2.printData(); test3.printData(); Student<Data<Info<int, int>, Info<int, string>, Info<string, string>>> stu(Data<Info<int, int>, Info<int, string>, Info<string, string>> (Info<int, int>(1,1), Info<int, string>(1,"sdsd"), Info<string, string>("sds","sdsd"))); stu.printData(); return 0; }