模板就是建立一个通用的模具,大大提高复用性
函数模板和类模板
泛型编程主要利用的技术就是模板
函数模板的作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体指定,用一个虚拟的类型来代表
提高复用性,使类型参数化
template<typename T>
函数声明或定义
/*
template--声明创建模板
typename--表明其后面的符号是一种数据类型,可以用class代替
T -- 通用的数据类型 名称可以替换
*/
基本使用
//函数模板
template <typename T>
void swap(T &t1,T &t2){
T temp = t1;
t1 = t2;
t2 = temp;
}
int a=5,b=10;
/*有两种使用函数模板
* 1.自动类型推导 swap(a,b); 即可
* 2.显示指定类型 swap<int>(a,b);
* */
swap<int>(a,b);
template <typename T>
void swap(T &t1,T &t2){
T temp = t1;
t1 = t2;
t2 = temp;
}
int a = 5;
char b = 'b';
swap(a,b); //错误的 不是一致的数据类型
template <typename T>
void func(){
cout<<"aaa"<<endl;
}
func();//不确定类型无法调用函数
从小到大排序
template<class T>
void sort(T arr[],int len){
for (int i = 0;i<len;++i){
int minIndex = i;
for (int j = i+1; j < len; ++j) {
if (arr[minIndex]>arr[j])
minIndex = j;
}
if (minIndex!=i){
swap<T>(arr[i],arr[minIndex]);
}
}
}
int arr[] = {1,45,20,3,9,5};
sort<int>(arr,6);
for (int i = 0; i < 6; ++i) {
std::cout<<arr[i]<<" ";
}
std::cout<<std::endl;
char arr0[] = {'a','c','z','r','f','z'};
sort<char>(arr0,6);
for (int i = 0; i < 6; ++i) {
std::cout<<arr0[i]<<" ";
}
int add(int a,int b){
return a+b;
}
int a=10,b=20;
char c='c';
std::cout<<add(a,b)<<std::endl;//30
std::cout<<add(a,c)<<std::endl;//109 'c'的ASCII码为97,自动转化
template<class T>
T myAdd(T a,T b){
return a+b;
}
std::cout<<myAdd(a,b)<<std::endl;//30
std::cout<<myAdd(a,c)<<std::endl;//报错 不能隐式类型转化
std::cout<<myAdd<int>(a,b)<<std::endl;//30
std::cout<<myAdd<int>(a,c)<<std::endl;//109 'c'的ASCII码为97,自动转化
建议使用函数模板使用显示指定
void myPrint(int a,int b){
std::cout<<"ordinary:"<<"a="<<a<<"&&b="<<b<<std::endl;
}
template <class T>
void myPrint(T a,T b){
std::cout<<"template:"<<"a="<<a<<"&&b="<<b<<std::endl;
}
template <class T>
void myPrint(T a,T b,T c){
std::cout<<"template:"<<"a="<<a<<"&&b="<<b<<"&&c="<<c<<std::endl;
}
int a=10,b=20,c=30;
myPrint(a,b);//ordinary:a=10&&b=20
// 调用的是普通函数 优先普通函数,如果普通函数只有声明,就会报错不会找函数模板
myPrint<>(a,b);//空模板 强制调用模板函数
myPrint<>(a,b,c);//函数模板重载
char h = 'h',g = 'g';
myPrint(h,g);//模板产生更好的匹配 优先函数模板
总结:如果存在函数模板了,最好就不要提高同名的普通函数了,否则容易出现二义性
模板通用性并不是万能的
template <class T>
void f(T a,T b){
a=b;
}
在上述代码中,如果传入的a和b是数组,那么无法实现了
C++为了解决这种问题,提供模板的重载,可以对特定的数据类型实现具体化
两个方法解决:
1、运算符重载 在类内部
2、重载函数模板 具体化模板
class P{
public:
int age;
std::string name;
P(int a,std::string s):age(a),name(s){}
};
template <class T>
bool myCompare(T &a,T &b){
return a==b;
}
//利用具体化P的版本实现代码 具体化优先调用
template<> bool myCompare(P &a,P &b){
if (a.age==b.age && a.name == b.name)
return true;
return false;
}
int a=10,b=10;
std::cout<<myCompare<int>(a,b)<<std::endl;
P p1(18,"tom");
P p2(18,"tom");
std::cout<<myCompare(p1,p2)<<std::endl;
实际上,学习模板并不是为了使用模板,而是为了STL中使用系统提供的模板
建立一个通用类,类中的成员 数据类型可以不具体指定,使用一个虚拟的类型来代表
//class可由typename代替 意思是一样的 其余意思和函数模板一样的含义
template <class T,class R>
类
template<class NameType,class AgeType>
class PER{
public:
NameType name;
AgeType age;
};
默认参数之后必须都是默认参数
template<class NameType=std::string ,class AgeType=int>
class PER{
public:
NameType name;
AgeType age;
PER():name("孙悟空"),age(999){}
PER(std::string n,int a):name(n),age(a){}
};
//但是还是要有 <> 不然编译器认为为自动类型推导 报错的
PER<>p("猪八戒",9999);
std::cout<<p.name<<std::endl;
类模板中的成员函数和普通成员函数创建时机是有区别:
有三种方法
template <class T1,class T2>
class Person1{
public:
T1 name;
T2 age;
Person1(T1 n,T2 a):name(n),age(a){}
void printInformation(){
std::cout<< this->name<<"&&"<< this->age<< std::endl;
}
};
//1.指定传入类型(最常用)
void fun1(Person1<std::string,int>&p){
p.printInformation();
}
//2.参数模板化
template <class T1,class T2>
void fun2(Person1<T1,T2> &p){
p.printInformation();
//模板的类型名称
std::cout<<"T1的类型:"<< typeid(T1).name()<<std::endl;
std::cout<<"T2的类型:"<< typeid(T2).name()<<std::endl;
}
//3.整个类模板化
template <class T>
void fun3(T &p){
p.printInformation();
std::cout<<"T的类型:"<< typeid(T).name()<<std::endl;
}
int main() {
Person1<std::string,int>p1("孙悟空",5252);
fun1(p1);
Person1<std::string,int>p2("猪八戒",1289);
fun2(p2);
Person1<std::string,int>p3("沙和尚",8289);
fun3(p3);
}
注意事项
template<class T>
class Father{
public:
T name;
};
//子类必须知道父类的T类型
class Son:public Father<std::string>{
};
//如果想要灵活的指定父类的T类型 子类也有变成模板
template <class T1,class T2>
class Son2:public Father<T1>{
public:
T2 age;
};
int main() {
Son2<std::string,int>s;
s.name = "孙悟空";
s.age = 888;
}
类模板中成员函数类外实现时,需要加上模板参数列表
template<class T1,class T2>
class An{
public:
T1 name;
T2 age;
An(T1 n,T2 a);
};
template<class T1,class T2>
An<T1,T2>::An(T1 n,T2 a){
this->name = n;
this->age=a;
}
问题:类模板中的成员函数创建时期是在调用阶段,导致分文件编写链接不到
解决方法
//hpp
#ifndef DU_H
#define DU_H
#include <iostream>
#include "string"
using namespace std;
template<class T1,class T2>
class Du {
public:
T1 myName;
T2 myAge;
Du(T1 name,T2 age);
void printInformation();
};
template<class T1,class T2>
Du<T1,T2>::Du(T1 name,T2 age) {
myName = name;
myAge = age;
}
template<class T1,class T2>
void Du<T1,T2>::printInformation() {
cout<< this->myName<<endl;
cout<< this->myAge<<endl;
}
#endif //DU_H
//类模板与友元
#include<iostream>
using namespace std;
template <class T1, class T2>
class Person;
//函数模板的实现
template <class T1, class T2>
void printPerson1(Person<T1, T2> p)
{
cout << p.id << p.age << endl;
}
template <class T1, class T2>
class Person
{
//类内友元 加上friend就是全局函数
friend void printPerson0(Person<T1, T2> p)
{
cout << p.id << p.age << endl;
}
//类外实现
//普通函数的声明 friend void printPerson1(Person<T1, T2> p);
//如果全局函数是类外实现,需要编译器提前知道模板函数的存在
friend void printPerson1<>(Person<T1, T2> p);
public:
Person(T1 a,T2 b)
{
this->id = a;
this->age = b;
}
private:
T1 id;
T2 age;
};
int main()
{
Person<int, int> p(1,2);
printPerson1(p);
return 0;
}
类内实现简单,如果没有特殊需求使用类内实现就可以了