一、函数模板
template — 声明创建模板
typename — 表面其后面的符号是一种数据类型,可以用class代替
T — 通用的数据类型,名称可以替换,通常为大写字母
#include <iostream>
using namespace std;
template<typename T>
void swapInt(T& a,T& b)
{
T temp = a;
a = b;
b = temp;
}
void test01()
{
int a = 10;
int b = 20;
swapInt(a,b);
cout<<"自动类型推导"<<endl;
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
swapInt<int>(a,b);
cout<<"显示指定类型"<<endl;
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
}
int main(){
test01();
system("pause");
return 0;
}
总结:
1.函数模板利用关键字 template
2.使用函数模板有两种方式:自动类型推导、显示指定类型
3.模板的目的是为了提高复用性,将类型参数化
注意事项:
1.自动类型推导,必须推导出一致的数据类型T,才可以使用
2.模板必须要确定出T的数据类型,才可以使用
排序函数模板
#include <iostream>
using namespace std;
#include <fstream>
template<typename T>
void mySwap(T& a,T& b)
{
T temp = a;
a = b;
b = temp;
}
template <class 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]);
}
}
}
template <typename T>
void printArray(T arr[],int len)
{
for(int i = 0;i < len;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}
void test01()
{
char charArr[] = "qwertyui";
int num = sizeof(charArr)/sizeof(char);
mySort(charArr,num);
printArray(charArr,num);
}
void test02()
{
int intArr[] = {7,4,9,11,3,6};
int num = sizeof(intArr)/sizeof(int);
mySort(intArr,num);
printArray(intArr,num);
}
int main(){
test01();
test02();
system("pause");
return 0;
}
输出结果:
y w u t r q i e
11 9 7 6 4 3
其实通过这个例子,很容易感觉得到,函数模板似乎和之前的多态有异曲同工之妙,都是同一件事物,却能够根据自己所需,实现相应的功能,甚至于会觉得像一个功能函数的调用
使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换
调用规则:
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,优先调用函数模板
但是提供了函数模板,最好就不要提供普通函数,否则容易出现二义性
--------------------**********************************************--------------------------------
--------------------**********************************************--------------------------------
二、类模板
和函数模板相类似的用法,template后面加上创建的类,一般类模板使用class,函数模板使用typename,以区分两种模板
类模板与函数模板区别主要有两点:
template<class NameType, class AgeType = int>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
void showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
public:
NameType mName;
AgeType mAge;
};
1.类模板没有自动类型推导的使用方式
void test01()
{
// Person p("孙悟空", 1000); // 错误 类模板使用时候,不可以用自动类型推导
Person <string ,int>p("孙悟空", 1000); //必须使用显示指定类型的方式,使用类模板
p.showPerson();
}
2.类模板在模板参数列表中可以有默认参数
void test02()
{
Person <string> p("猪八戒", 999); //类模板中的模板参数列表 可以指定默认参数
p.showPerson();
}
int main() {
test01();
test02();
system("pause");
return 0;
}
而且,类模板中成员函数和普通类中成员函数创建时机是有区别的:
1.普通类中的成员函数一开始就可以创建
2.类模板中的成员函数在调用时才创建
所以,类模板中的成员函数并不是一开始就创建的,在调用时才去创建
同时,类模板对象也可以做函数参数,传入函数一共有三种方式:
template<class NameType, class AgeType = int>
class Person
{
public:
Person(NameType name, AgeType age)
{
this->mName = name;
this->mAge = age;
}
void showPerson()
{
cout << "name: " << this->mName << " age: " << this->mAge << endl;
}
public:
NameType mName;
AgeType mAge;
};
1.指定传入的类型 ,直接显示对象的数据类型
void printPerson1(Person<string, int> &p)
{
p.showPerson();
}
void test01()
{
Person <string, int >p("孙悟空", 100);
printPerson1(p);
}
2.参数模板化,将对象中的参数变为模板进行传递
template <class T1, class T2>
void printPerson2(Person<T1, T2>&p)
{
p.showPerson();
cout << "T1的类型为: " << typeid(T1).name() << endl;
cout << "T2的类型为: " << typeid(T2).name() << endl;
}
void test02()
{
Person <string, int >p("猪八戒", 90);
printPerson2(p);
}
3.整个类模板化,将这个对象类型 模板化进行传递
template<class T>
void printPerson3(T & p)
{
cout << "T的类型为: " << typeid(T).name() << endl;
p.showPerson();
}
void test03()
{
Person <string, int >p("唐僧", 30);
printPerson3(p);
}
另外,类模板与普通类一样可以用于继承,需要注意的是;
1.当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
2.如果不指定,编译器无法给子类分配内存
3.如果想灵活指定出父类中T的类型,子类也需变为类模板
如果父类是类模板,子类需要指定出父类中T的数据类型
template<class T>
class Base
{
T m;
};
//class Son:public Base //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> //必须指定一个类型
{
};
void test01()
{
Son c;
}
//类模板继承类模板 ,可以用T2指定父类中的T类型
template<class T1, class T2>
class Son2 :public Base<T2>
{
public:
Son2()
{
cout << typeid(T1).name() << endl;
cout << typeid(T2).name() << endl;
}
};
void test02()
{
Son2<int, char> child1;
}
通常来讲,我们都习惯将函数声明写在.h头文件中,函数的实现写在.c源文件中,然而对于类模板,函数声明与实现如果分开写,会导致程序链接不到,所以解决办法之一就是直接包含写有模板的源文件,但还有更好的办法,那就是将声明和实现写在同一个文件,文件后缀改为.hpp,属于模板文件
hello.cpp
#include <iostream>
using namespace std;
#include "hello.hpp"
void test01()
{
Person<string,int> p("Jack",18);
p.showPerson();
}
int main(){
test01();
system("pause");
return 0;
}
hello.hpp
#pragma once
#include <iostream>
using namespace std;
#include <string>
template<class T1,class T2>
class Person
{
public:
Person(T1 name,T2 age);
void showPerson();
public:
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<<"name:"<<this->m_Name;
cout<<"age:"<<this->m_Age;
}
最后是关于友元函数与类模板,友元函数可以在类内实现也可以在类外实现
1.类外实现
#include <iostream>
using namespace std;
#include <string>
//1.先声明一下类模板
template<class T1,class T2> class Person;
//2.友元函数的类外实现(全局函数)
template<class T1,class T2>
void printPerson1(Person<T1,T2> &p)
{
cout<<"友元函数类内声明,类外实现"<<endl;
}
template<class T1,class T2>
class Person
{
//3.友元函数的类内声明
friend void printPerson1<>(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;
};
void test01()
{
Person<string,int> p("史蒂芬",21);
printPerson1(p);
}
int main(){
test01();
system("pause");
return 0;
}
2.类内实现(推荐使用这种)
template<class T1,class T2>
class Person
{
friend void printPerson1(Person<T1,T2> &p)
{
cout<<"友元函数类内实现"<<endl;
}
public:
Person(T1 name,T2 age)
{
this->m_Name = name;
this->m_Age = age;
}
private:
T1 m_Name;
T2 m_Age;
};