C++ 之函数模板
函数的参数类型不确定,这样就可以使用泛型。
| void mySwap(T &a, T &b) { |
| T tmp = a; |
| a = b; |
| b = tmp; |
| } |
自动类型推导,函数必须有参数才可以推导
| |
| mySwap<int>(a, b); |
| cout << "a=" << a << "," << "b=" << b << endl; |
| |
template < typename T>
void mySwap1() {
}
mySwap1 < double >(); // 如果不能根据参数列表推出来,就得显示指定类型
2 使用函数模板实现通用数组排序
| #define _CRT_SECURE_NO_WARNINGS |
| #include <iostream> |
| using namespace std; |
| |
| |
| |
| |
| |
| |
| template<class T> |
| void mySort(T arr[],int len) { |
| |
| for (int i = 0; i < len-1;i++) { |
| |
| int max = i; |
| for (int j = i + 1; j < len;j++) { |
| if (arr[max] < arr[j]) { |
| |
| |
| max = j; |
| } |
| } |
| if (max != i) { |
| |
| swap(arr[max],arr[i]); |
| } |
| } |
| } |
| |
| |
| template<class T> |
| void printArray(T arr[],int len) { |
| for (int i = 0; i < len;i++) { |
| cout << arr[i] << "\t"; |
| } |
| } |
| void test01() { |
| char arr[] = "chengluofei"; |
| |
| mySort(arr,strlen(arr)); |
| printArray(arr, strlen(arr)); |
| } |
| |
| int main() |
| { |
| test01(); |
| system("pause"); |
| return EXIT_SUCCESS; |
| } |
3 普通函数与函数模板的区别以及调用规则
| #define _CRT_SECURE_NO_WARNINGS |
| #include <iostream> |
| using namespace std; |
| |
| |
| template<class T> |
| T myPlus(T a,T b) { |
| return a + b; |
| } |
| |
| int myPlus2(int a , int b) { |
| return a + b; |
| } |
| |
| void test01() { |
| int a = 10; |
| int b = 20; |
| cout << myPlus(a, b) << endl; |
| cout << myPlus2(a, b) << endl; |
| int c = 'c'; |
| |
| |
| |
| |
| cout << myPlus2(a, c) << endl; |
| } |
| |
| |
| template<class T> |
| void myPrint(T a, T b) |
| { |
| cout << "模板调用的myPrint" << endl; |
| } |
| |
| template<class T> |
| void myPrint(T a, T b,T c) |
| { |
| cout << "模板调用的myPrint(a,b,c)" << endl; |
| } |
| |
| void myPrint(int a, int b) |
| { |
| cout << "函数调用的myPrint" << endl; |
| } |
| |
| void test02() { |
| |
| int a = 10; |
| int b = 20; |
| |
| |
| |
| |
| |
| myPrint<>(10,20); |
| |
| |
| myPrint<>(10, 20,30); |
| |
| |
| char c = 'c', d = 'd'; |
| myPrint(c,d); |
| } |
| |
| int main() |
| { |
| test01(); |
| test02(); |
| system("pause"); |
| return EXIT_SUCCESS; |
| } |
4 模板机制
函数模板通过不同的类型产生不同的函数。
编译器会对函数模板进行两次编译。在声明的地方对模板本身进行编译, 在调用的时候对参数进行替换后再进行一次编译。
STL 90%以上都是使用模板来做的。
通过模板产生的函数就叫模板函数。(为函数模板传值时,会重新生成对应类型的函数。)
模板并不是万能的,不能通用所有的数据类型。
模板不能直接调用, 生成后的模板函数才可以调用。
二次编译,第一次对模板进行编译,第二次对替换T类型后的函数进行编译。
5 函数模板的局限性
template < class T >
void f(T a, T b){ …. }
如果T是数组的话, 就没办法使得a = b语句成立。if(a > b) 语句也不能成立。
| #define _CRT_SECURE_NO_WARNINGS |
| #include <iostream> |
| using namespace std; |
| #include <string> |
| #include <vector> |
| |
| |
| class Person { |
| public: |
| Person(string name, int age) :m_Name(name), m_Age(age) { |
| |
| } |
| |
| string m_Name; |
| int m_Age; |
| |
| }; |
| |
| 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.m_Age == b.m_Age) { |
| return true; |
| } |
| else return false; |
| } |
| |
| void test01() { |
| int a = 10; |
| int b = 20; |
| |
| bool res = myCompare(a,b); |
| cout << res << endl; |
| |
| Person p1("Tom",3); |
| Person p2("Jerry",4); |
| |
| bool res2 = myCompare(p1, p2); |
| cout << res2 << endl; |
| } |
| |
| int main() |
| { |
| test01(); |
| system("pause"); |
| return EXIT_SUCCESS; |
| } |
6 类模板的基本使用
| |
| template <class NameType,class AgeType> |
| class Person { |
| |
| public: |
| Person(NameType name, AgeType age) { |
| this->m_Name = name; |
| this->m_Age = age; |
| } |
| |
| void showPerson() { |
| cout <<"姓名:"<<this->m_Name <<" 年龄:"<< this->m_Age << endl; |
| } |
| |
| NameType m_Name; |
| AgeType m_Age; |
| }; |
类模板支持默认参数的(默认的类型),函数模板不可以。
template <class NameType = string ,class AgeType= int>
如果这里声明了默认的类型,那么创建对象的时候,不需要在菱形中显示声明类型。
Person <> p("张三",12);
7 成员函数的创建时机
| class Person1 { |
| public: |
| void showPerson1() { |
| cout << "Person1的调用" << endl; |
| } |
| }; |
| |
| class Person2 { |
| public: |
| void showPerson1() { |
| cout << "Person2的调用" << endl; |
| } |
| }; |
| |
| template <class T> |
| class Myclass { |
| |
| public: |
| |
| T obj; |
| void func1() { |
| obj.showPerson1(); |
| } |
| |
| void func2() { |
| obj.showPerson2(); |
| } |
| }; |
| |
| |
| void test02() { |
| |
| Myclass<Person1> mc; |
| mc.func1(); |
| mc.func2(); |
| } |
8 类模板做函数的参数
| |
| void doWark(Person<string,int> &p) { |
| p.showPerson(); |
| } |
| |
| template<class T1,class T2> |
| void doWork2(Person<T1,T2> & p) { |
| |
| cout << typeid(T1).name() << endl; |
| cout << typeid(T2).name() << endl; |
| p.showPerson(); |
| } |
| |
| void test01() { |
| Person<string,int> p("张三",23); |
| doWark(p); |
| doWork2(p); |
| } |
| |
| |
| |
| template <class T> |
| void doWork3(T &p) { |
| p.showPerson(); |
| } |
| |
| void test02() { |
| Person<> p("孙悟空",500); |
| doWork3(p); |
| } |
9 类模板遇到继承问题以及解决办法
子类继承父类(此时父类是模板类)的时候,需要指定父类模板中的类型,不然创建子类对象的时候, 会默认调用父类的构造器,导致父类无法给未知类型的变量分配内存。
| template <class T> |
| class Base { |
| public: |
| T m_A; |
| }; |
| |
| class Child : public Base<int> { |
| |
| }; |
| template <class T1, class T2> |
| class Child2 : public Base<T2> { |
| |
| public: |
| Child2() { |
| cout << typeid(T1).name() << endl; |
| cout << typeid(T2).name() << endl; |
| } |
| T1 m_B; |
| }; |
| void test01() { |
| Child2<int, double> child; |
| } |
10 类模板类外实现成员函数
| template <class T1,class T2> |
| class Person { |
| public: |
| Person(T1 name, T2 age); |
| |
| void showPerson(); |
| |
| private: |
| T1 m_name; |
| T2 m_age; |
| }; |
| |
| template <class T1, class T2> |
| Person<T1, T2>::Person(T1 name, T2 age) { |
| this->m_name = name; |
| this->m_age = age; |
| } |
| |
| template<class T1, class T2> |
| void Person<T1, T2>::showPerson() |
| { |
| cout << "姓名:" << this->m_name << ", 年龄:" << this->m_age << endl; |
| } |
11 类模板分文件编写以及问题解决

运行后,保持解决方法: 将Person.h 改为 Person.cpp
分文件写的话,因为是模板函数,代码一开始并不成成模板函数,因为确定不了类型。(C++是采用单元编译的。)
Person.h文件:
| #pragma once |
| #include <iostream> |
| using namespace std; |
| #include <string> |
| |
| template <class T1, class T2> |
| class Person { |
| public: |
| Person(T1 name, T2 age); |
| void showPerson(); |
| |
| private: |
| T1 m_name; |
| T2 m_age; |
| }; |
编译的时候, 编译检查Person.h文件,语法没有任何问题。 接着,检查Person.cpp文件。
| #include "Person.h" |
| template <class T1, class T2> |
| Person<T1, T2>::Person(T1 name, T2 age) { |
| this->m_name = name; |
| this->m_age = age; |
| } |
| |
| template <class T1, class T2> |
| void::showPerson() { |
| cout << "姓名:" << this->m_name << ", 年龄:" << this->m_age << endl; |
| } |
因为Person.cpp文件里面全是函数目版,编译器不生成模板函数,所以对应的Person.h中的具体实现也就实现不了。所以 在主函数里面,直接导入对应的cpp文件就可解决上面问题。
建议模板不要做分文件编写,一般写到一个类即可,全写到一个文件Person.h文件中,类内声明,类内实现。再把.h后缀改为.hpp.
main.cpp文件:
| #define _CRT_SECURE_NO_WARNINGS |
| #include <iostream> |
| using namespace std; |
| #include "Person.hpp" |
| |
| void test() { |
| Person<string, int> p1("张三",12); |
| p1.showPerson(); |
| } |
| |
| int main() { |
| |
| test(); |
| system("pause"); |
| return EXIT_SUCCESS; |
| } |
.hpp文件一般是写模板用的。

12 友元函数碰到类模板-友元函数类内实现
如果函数和普通函数都能匹配到的话,优先调用普通函数。
| template <class T1, class T2> |
| class Person { |
| |
| |
| friend void printPerson(Person<T1, T2> &p); |
| public: |
| Person(T1 name, T2 age) { |
| this->name = name; |
| this->age = age; |
| } |
| private: |
| T1 name; |
| T2 age; |
| |
| }; |
| |
| template <class T1, class T2> |
| void printPerson(Person<T1, T2> &p) { |
| cout << "姓名:" << p.name << "年龄" << p.age << endl; |
| } |
| |
| void test() { |
| Person<string, int> p("张三", 12); |
| printPerson(p); |
| } |
解决方案: 在友元函数函数名处加上菱形括号,表示模板函数的声明。
| |
| |
| 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->name = name; |
| this->age = age; |
| } |
| private: |
| T1 name; |
| T2 age; |
| |
| }; |
| |
| template <class T1, class T2> |
| void printPerson(Person<T1, T2> &p) { |
| cout << "姓名:" << p.name << "年龄" << p.age << endl; |
| } |
| |
| void test() { |
| |
| Person<string, int> p("张三", 12); |
| printPerson(p); |
| } |
| |
| int main() |
| { |
| test(); |
| system("pause"); |
| return EXIT_SUCCESS; |
| } |
13 类模板的应用-数组类封装
MyArray.hpp文件
| template <class T> |
| class MyArray { |
| public: |
| |
| explicit MyArray(int capacity) { |
| this->m_Capacity = capacity; |
| this->m_Size = 0; |
| this->pAddress = new T[this->m_Capacity]; |
| } |
| |
| MyArray(const MyArray & arry) { |
| this->m_Capacity = arry.m_Capacity; |
| this->m_Size = arry.m_Size; |
| this->pAddress = new T[this->m_Capacity]; |
| |
| for (int i = 0; i < this->m_Size;i++) { |
| this->pAddress[i] = arry[i]; |
| } |
| } |
| |
| ~MyArray() { |
| if (this->pAddress != NULL) { |
| delete[] this->pAddress; |
| this->pAddress = NULL; |
| } |
| } |
| |
| |
| void operator=(MyArray & arry) { |
| |
| |
| if (this->pAddress != NULL) { |
| delete[] this->pAddress; |
| this->pAddress = NULL; |
| } |
| |
| this->m_Capacity = arry.m_Capacity; |
| this->m_Size = arry.m_Size; |
| this->pAddress = new T[this->m_Capacity]; |
| |
| for (int i = 0; i < this->m_Size; i++) { |
| this->pAddress[i] = arry[i]; |
| } |
| |
| } |
| |
| |
| T & operator[](int index){ |
| return this->pAddress[index]; |
| } |
| |
| |
| void pushBack(T val) { |
| |
| this->pAddress[this->m_Size] = val; |
| this->m_Size++; |
| } |
| |
| |
| int getSize() { |
| |
| return this->m_Size; |
| } |
| |
| |
| void printArry() { |
| for (int i = 0; i < this->m_Size;i++) { |
| cout << this->pAddress[i] << " "; |
| } |
| cout << endl; |
| } |
| |
| private: |
| T * pAddress; |
| int m_Capacity; |
| int m_Size; |
| }; |
主函数.cpp文件:
| class Person { |
| public: |
| Person() { |
| cout << "调用默认的构造函数" << endl; |
| } |
| |
| Person(string name,int age) { |
| this->m_Name = name; |
| this->m_age = age; |
| } |
| |
| ~Person() { |
| |
| cout << "调用析构函数" << endl; |
| } |
| string m_Name; |
| int m_age; |
| }; |
| |
| void printPersonArray(MyArray<Person> &arr) { |
| |
| for (int i = 0; i < arr.getSize();i++) { |
| cout << "姓名:" << arr[i].m_Name << " 年龄" << arr[i].m_age << endl; |
| } |
| } |
| |
| void test() { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| Person p1("张三",12); |
| Person p2("李四", 13); |
| Person p3("王五", 14); |
| Person p4("赵六", 15); |
| |
| MyArray<Person> pArray(10); |
| pArray.pushBack(p1); |
| pArray.pushBack(p2); |
| pArray.pushBack(p3); |
| pArray.pushBack(p4); |
| |
| printPersonArray(pArray); |
| |
| } |
| int main() |
| { |
| test(); |
| system("pause"); |
| return EXIT_SUCCESS; |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!