💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 一、引言 在写排序算法时,如果要写一个交换两个数据的函数,由于基本数据类型有int、float、double等等类型,所以针对每种数据类型可能我们要写很多swap函数,这些函数除了传入的数据类型不同,函数体结构大致相同。所以C++为了避免让程序员写很多大量重复代码,设计了一种叫做“模板”的东西。我们写程序时,先不指定什么类型,在调用时我们再说明一下是什么类型,具体怎么实现接着往下看。 # 二、函数模板 ## 1、定义 像开头所说,如果要对int、double类型的两个数进行交换我们要写两个函数,但用函数模板时只需要写一个函数即可。模板定义如下: `template <typename T>` 或者 `template <class T>` 其中,template是声明一个模板,typename是声明一个虚类型T,这个T可以代替int、double等基本数据类型,那为什么还有class?因为最初是用class声明一个T来表示通用数据类型,但考虑到class会和“类”混淆,就改用typename进行定义,同时保留了class,这两者效果相同,但我个人比较习惯用class。 在进行声明模板之后下面就开始写模板的函数体了,例如交换函数,合起来就是: ~~~java template <class T> void swap(T *p1,T *p2){ T temp=*p1; *p1=*p2; *p2=temp; } ~~~ 这样就是一个完整的函数模板 ## 2、调用 有两种方式使用模板 (1)自动类型推导:编译器会自动判定你传入的数据是什么数据类型,然后将T改成对应数据类型进行操作; (2)自己声明数据类型进行调用,具体实现如下: ~~~java //1、自动类型推导 swap(a, b); cout << "a=" << a << endl; cout << "b=" << b << endl; //2、显示指定类型 swap<int>(a, b); cout << "a=" << a << endl; cout << "b=" << b << endl; ~~~ **何时必须自己声明数据类型呢?** **那就是没有参数传递的时候**。比如知识一个打印函数,没有参数传递,这里就不写代码了。 ## 3、多个虚类型 前面是针对一个函数里面都是同一数据类型的,如果含有不同数据类型,假如有两个: ~~~ template<class T1,class T2> void func(T1 a,T2 b){....} ~~~ 调用时可以自动识别类型也可以自己声明数据类型: ~~~ func<int,double>(1,3.14); ~~~ # 三、类模板 前面已经介绍了模板大致的作用,这里对类模板就不做过多说明,直接上干货: ## 1、定义 我们定一个学生类 ~~~java template<class T1,class T2,class T3> class Student{ public: Student(T1 name,T2 age,T3 score){ .......... } T1 m_Name; T2 m_Age; T3 m_Score; } ~~~ ## 2、调用 **调用时必须指定输入了什么数据,也就是尖括号不能省略,这点与函数模板不一样** ~~~ Student<string,int,float>s("Tom",18,85.5); ~~~ # 四、类的函数模板 在类内定义的话就跟前面的函数模板一样,**如果在类外定义,类外也要写上函数模板的形式:** ~~~java template<class numType> class Compare{ public: Compare(numType a,numType b){ this->a=a; this->b=b; } //声明模板函数: numType max( ); private: numType a; numTypr b; } //类外定义模板函数 template<class numType> numType Compare::max( ){ return this->a>this->b?this->a:this->b; } ~~~ # 五、类作为数据类型传入 ~~~java //定义Person1 class Person1 { public: void showPerson1() { cout << "Person1 show" << endl; } }; //定义Person2 class Person2 { public: void showPerson2() { cout << "Person2 show" << endl; } }; //定义类模板 template<class T> class MyClass { public: T obj; //类模板中的成员函数 void func1() { obj.showPerson1(); } void func2() { obj.showPerson2(); } }; //主函数 int main() { MyClass<Person1>m; m.func1(); //m.func2();//会报错,因为“showPerson2”: 不是“Person1”的成员 system("pause"); return 0; } ~~~ # 六、类模板与继承 如果父类是一个模板,子类继承父类模板的时候,不知道父类模板内存是多少,编译器就无法给子类分配内存,解决方案是给子类也加上模板,方案如下: ~~~java template<class T> class Base { T m_Name; }; //class Son :public Base { //错误,必须知道T的内存才能指定空间 class Son1:public Base<int>{ // 不灵活 }; //想要灵活指定父类中T的类型 template<class T1,class T2> class Son2 :public Base<T2> { public: T1 m_A; }; int main() { //让子类的T为string,子类的T1为int Son2<int, string>s2; system("pause"); return 0; } ~~~ # 七、类模板与友元 前面也有过**传入类对象到函数里面,如果这个函数需要用到对象里面的数据,而该数据有被设定为private(私有)就无法直接访问**,此时又要用到友元函数的知识: **注意:类内添加友元函数后,如果没有对该友元函数进行声明,编译器认为你没有这个函数,会进行报错。** ~~~java //先声明类和函数,防止编译器报错 template<class T1,class T2> class Person; template<class T1, class T2> void printPerson(Person<T1, T2> p); template<class T1,class T2> class Person { //全局函数类外实现的声明 friend void printPerson<>(Person<T1, T2> p); public: Person(T1 name, T2 age) { this->m_Name = name; this->m_Age = age; } private: T1 m_Name; T2 m_Age; }; //全局函数类外实现 template<class T1,class T2> void printPerson(Person<T1, T2> p) { cout << "Name:" << p.m_Name << endl; cout << "Age:" << p.m_Age << endl; } ~~~ 觉得有用记得顶一下哦\_