模版就是建立一个通用的模具,大大提高复用性。
函数模板的作用:建立一个通用的函数,其函数返回值类型和形参列表可以不具体确定,用一个虚拟的类型来代表。
目的:将类型参数化。
语法:
template<typename T> //函数的声明或定义
template:声明创建模板
typename:表明其后面的是一种数据类型,可以用class替代
T:通用的数据类型,名称可以替换,通常为大写字母
普通函数调用时可以发生自动类型转换(隐式类型转换)。
模板并不是万能的。
例一:交换两个数据,根据类型分别写对应函数。
- #include<iostream>
- using namespace std;
- //交换两个整形数据
- void swapInt(int& a, int& b)
- {
- int temp = a;
- a = b;
- b = temp;
- }
- //交换两个浮点型数据
- void swapDouble(double& a, double& b)
- {
- double temp = a;
- a = b;
- b = temp;
- }
- void test01()
- {
- int a = 10;
- int b = 20;
- swapInt(a, b);
- cout << a << endl;
- cout << b << endl;
- double c = 1.1;
- double d = 2.2;
- swapDouble(c,d);
- cout << c << endl;
- cout << d << endl;
- }
- int main()
- {
- test01();
- }
例二:使用函数模板完成任意类型数据的相加
- #include<iostream>
- using namespace std;
- //交换两个T型数据
- template<typename T>
- void Myswap(T& a, T&b)
- {
- T temp = a;
- a = b;
- b = temp;
- }
- void test01()
- {
- int a = 10;
- int b = 20;
- //1、自动类型推导
- Myswap(a, b);
- //2、显示指定类型
- //Myswap<int>(a,b);
- cout << a << endl;
- cout << b << endl;
- }
- int main()
- {
- test01();
- }
注意:模板必须确定T的数据类型,才可以使用。且数据类型必须一致。例如上例中将整形和字符型进行交换,数据类型不同将会报错。
例:利用函数模板对不同数据类型的数组进行从大到小的选择排序。
- #include<iostream>
- using namespace std;
- //交换两个T型数据
-
- template<typename T>
- void mySwap(T&a,T&b)
- {
- T temp = a;
- a = b;
- b = temp;
- }
- template<typename T>
- void printArr(T arr[], int len)
- {
- for (int i = 0; i < len; i++)
- {
- cout << arr[i] << " ";
- }
- cout << endl;
- }
-
- template<typename T>
- void Mysort(T arr[], int len)
- {
- for (int i = 0; i < len; i++)
- {
- int max = i;
- for (int j = i + 1; j < len; j++)
- {
- if (arr[max] < arr[j])
- {
- max = j;
- }
- }
- if (max != i)
- {
- mySwap(arr[max], arr[i]);
- }
- }
- }
- void test01()
- {
- char charArr[] = "badcfe"; //测试char数组
- int num = sizeof(charArr) / sizeof(char);
- Mysort(charArr, num);
- printArr(charArr, num);
- }
- int main()
- {
- test01();
- }
作用:建立一个通用的类,类中的成员函数和数据类型可以不具体指定,用一个虚拟的类型代表。
语法:
Template<typename T>
类
template<typename T> //声明创建模板
template:声明创建模板
typename:表明其后面的是一种数据类型,可以用class替代
T:通用的数据类型,名称可以替换,通常为大写字母
类模板的传入参数也需要在确定对象时也需要初始化。
- #include<iostream>
- #include"string"
- using namespace std;
-
-
- template<class NameType, class AgeType>
- class Person
- {
- public:
- Person(NameType name, AgeType age)
- {
- this->m_Name = name;
- this->m_Age = age;
- }
- void showPerson()
- {
- cout << "Name: " << this->m_Name << endl;
- cout << "Age: " << this->m_Age << endl;
- }
- NameType m_Name;
- AgeType m_Age;
- };
-
- void test01()
- {
- Person<string, int> p1("wxq", 29);
- //<>模版的参数列表
- p1.showPerson();
- }
- int main()
- {
- test01();
- }
1、类模板没有自动类型推导的使用方式
- #include<iostream>
- #include"string"
- using namespace std;
-
-
- template<class NameType, class AgeType>
- class Person
- {
- public:
- Person(NameType name, AgeType age)
- {
- this->m_Name = name;
- this->m_Age = age;
- }
- void showPerson()
- {
- cout << "Name: " << this->m_Name << endl;
- cout << "Age: " << this->m_Age << endl;
- }
- NameType m_Name;
- AgeType m_Age;
- };
- //类模板没有自动推导类型
- void test01()
- {
- //Person p1("wxq", 29);
- //错误,无法用自动类型推导
-
- Person<string, int> p2("wxq", 29);
- //只能用显示指定类型
- p2.showPerson();
- }
- int main()
- {
- test01();
- }
2、类模板在模板参数列表中可以有默认参数
- #include<iostream>
- #include"string"
- using namespace std;
-
-
- template<class NameType, class AgeType=int>
- class Person
- {
- public:
- Person(NameType name, AgeType age)
- {
- this->m_Name = name;
- this->m_Age = age;
- }
- void showPerson()
- {
- cout << "Name: " << this->m_Name << endl;
- cout << "Age: " << this->m_Age << endl;
- }
- NameType m_Name;
- AgeType m_Age;
- };
- //类模板在模板参数列表中可以有默认参数
- void test01()
- {
- Person<string> p2("wxq", 29);
- //只能用显示指定类型
- p2.showPerson();
- }
- int main()
- {
- test01();
- }
类模板中的成员函数和普通类中的成员函数的创建时机是不同的:普通类的成员函数一开始就可以创建成员函数;类模板中的成员函数在调用时创建。
- #include<iostream>
- #include"string"
- using namespace std;
- class Person1
- {
- public:
- void showPerson1()
- {
- cout << "Person1 shows!" << endl;
- }
- };
- class Person2
- {
- public:
- void showPerson2()
- {
- cout << "Person2 shows!" << endl;
- }
- };
- template<class T>
- class Person
- {
- public:
- T obj;
- void func1()
- {
- obj.showPerson1();
- }
- void func2()
- {
- obj.showPerson2();
- }
- };
-
- void test01()
- {
- Person<Person1> m;
- m.func1();
- //m.func2(); //报错,因为m中无showPerson2函数
- }
- int main()
- {
- test01();
- }
类模板实例出的对象作为函数的实参,一共有三种传入方式:
1、指定类型的传入:直接显示对象的数据类型(主要使用)
2、参数模板化:将对象中的参数变为模板进行传递(类模板配合函数模版)
3、整个类模板化:将这个对象类型模板化进行传递
- #include<iostream>
- #include"string"
- using namespace std;
- template<class T1, class T2>
- class Person
- {
- public:
- Person(T1 name, T2 age)
- {
- this->m_Name = name;
- this->m_Age = age;
- }
- void showPerson()
- {
- cout << "name is " <<m_Name<< endl;
- cout << "age is " << m_Age<<endl;
- }
- T1 m_Name;
- T2 m_Age;
- };
- //1、指定传入类型
- void printPerson1(Person<string,int>& p)
- {
- p.showPerson();
- }
- void test01()
- {
- Person<string, int> p("Wang", 100);
- printPerson1(p);
- }
- //2、将参数模板化
- template<class T1, class T2>
- void printPerson2(Person<T1, T2>& p)
- {
- p.showPerson();
- }
- void test02()
- {
- Person<string, int> p("Li", 90);
- printPerson2(p);
- }
- //3、整个类模板化
- template<class T>
- void printPerson3(T& p)
- {
- p.showPerson();
- }
- void test03()
- {
- Person<string, int> p("Zhang", 80);
- printPerson3(p);
- }
- int main()
- {
- test01();
- test02();
- test03();
- }
需要注意几个点:
1、当子类继承的父类是一个类模板时,子类在声明时需要指定父类中T的类型;如果不指定,编译器则无法给子类分配内存。
2、如果想灵活指定父类中的T的类型,子类也需要变为类模板。
如果父类是类模板,子类在继承时需要指定父类中的数据类型。
- #include<iostream>
- #include"string"
- using namespace std;
- template<class T>
- class Base
- {
- T m;
- };
- //class Son :public Base //错误,必须知道父类的T的数据类型,才能继承给子类
- //class Son :public Base<int> //正确,但是子类只能为int
- template<class T1, class T2>
- class Son :public Base<T2>
- {
- T1 obj;
- };
- void test01()
- {
- Son<int,char> s1; //int传给T1,char传给T2,父类中的T也为char
- }
- int main()
- {
- test01();
- }
- #include<iostream>
- #include"string"
- using namespace std;
- template<class T1, class T2>
- class Person
- {
- public:
- Person(T1 name, T2 age);
- void show();
- 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>::show()
- {
- cout << "Name is " << this->m_Name << endl;
- cout << "Age is " << this->m_Age << endl;
- }
- void test01()
- {
- Person<string, int> p("Wang", 100);
- p.show();
- }
- int main()
- {
- test01();
- }
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到。
方法:
1、直接包含CPP源文件
2、将声明个实现写在同一个文件中,并更改后缀名为.hpp。hpp是一个约定的概念,表示的就是类模板的分文件编写。(用的多)
方法1:
| Person.h | Person.cpp | Main.cpp |
| #pragma once #include<iostream> #include"string" using namespace std; template<class T1, class T2> class Person { public: Person(T1 name, T2 age); void show(); T1 m_Name; T2 m_Age; }; | #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 Person<T1, T2>::show() { cout << "Name is " << this->m_Name << endl; cout << "Age is " << this->m_Age << endl; } | #include"Person.cpp" //包含的是cpp,不是h //第二种解决方法是把.h和.cpp文件写在一起,将后缀名改为.hpp文件 void test01() { Person<string, int> p("Wang", 100); p.show(); } int main() { test01(); } |
方法2
| Person.hpp | Main.cpp |
| #pragma once #include<iostream> #include"string" using namespace std; template<class T1, class T2> class Person { public: Person(T1 name, T2 age); void show(); 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>::show() { cout << "Name is " << this->m_Name << endl; cout << "Age is " << this->m_Age << endl; } | #include"Person.hpp" //包含的是cpp,不是h //第二种解决方法是把.h和.cpp文件写在一起,将后缀名改为.hpp文件 void test01() { Person<string, int> p("Wang", 100); p.show(); } int main() { test01(); } |
主要包括友元函数的类内实现和类外实现。类内直接声明友元即可,类外实现则需要提前让编译器指导全局函数的存在。
case 1:友元函数在类内实现
- #include<iostream>
- #include"string"
- using namespace std;
- template<class T1,class T2>
- class Person
- {
- public:
- Person(T1 name, T2 age)
- {
- this->m_Name = name;
- this->m_Age = age;
- }
- friend void show(Person<T1, T2> p)
- {
- cout << "Name is " <<p.m_Name << endl;
- cout << "Age is " << p.m_Age << endl;
- }
- private:
- T1 m_Name;
- T2 m_Age;
- };
- //1、全局函数在类内实现
- void test01()
- {
- Person<string, int> p("Wang", 100);
- show(p);
- }
- int main()
- {
- test01();
- }
case 2:友元函数在类外实现
- #include<iostream>
- #include"string"
- using namespace std;
- //提前让编译器知道person类的存在
- template<class T1, class T2>
- class Person;
-
- //2、全局函数类外实现,先让编译器知道有个类外函数的存在
- template<class T1, class T2>
- void show(Person<T1, T2> p)
- {
- cout << "Name is " << p.m_Name << endl;
- cout << "Age is " << p.m_Age << endl;
- }
-
- template<class T1,class T2>
- class Person
- {
- public:
- Person(T1 name, T2 age)
- {
- this->m_Name = name;
- this->m_Age = age;
- }
- //加空模板的参数列表 <>
- //如果全局函数是类外实现,需要让编译器提前知道这个函数的存在
- friend void show<>(Person<T1, T2> p);
- private:
- T1 m_Name;
- T2 m_Age;
- };
-
- void test01()
- {
- Person<string, int> p("Wang", 100);
- show(p);
- }
- int main()
- {
- test01();
- }
类外实现比较复杂,首先需要让编译器知道有一个全局函数的存在,由于用到了person,要先让编译器知道person类存在,person类又是一个类模板,因此还得声明类模板。
如无特殊需求,尽量类内实现。