一、模板的作用
建立一个通用函数,其函数返回值类型和形参类型可以不具体确定,用一个虚拟的类型来代表。
template<typename T>
二、示例
1、不使用模板写两个交换变量数值
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;
}
int main()
{
int a = 10;
int b = 20;
SwapInt(a, b);
cout << a << endl;
cout << b << endl;
system("pause");
return 0;
}
2、使用模板交换两个变量数值
template<typename T>
void Swap(T& a,T&b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10;
int b = 20;
Swap(a, b); //自动推导类型
Swap<int>(a, b); //显示指定类型
cout << a << endl;
cout << b << endl;
system("pause");
return 0;
}
三、使用模板时的注意事项
1、自动类型推导,必须推导出一致的数据类型T,才能使用
int a = 10;
int b = 20;
char c = 'c';
Swap(a, b); //自动推导类型
//Swap(a, c); //报错,自动推导时类型必须相同
2、模板必须要确定出T的数据类型,才可以使用
template<typename T>
void func()
{
cout << "111" << endl;
}
int main()
{
//func(); //错误,模板不能独立使用,必须确定出T的类型
func<int>(); //利用显示置定类型的方法,给T一个类型,才可以使用模板
system("pause");
return 0;
}
四、函数模板案例
案例描述:
①利用函数模板封装一个排序函数,可以对不同数据类型数组进行排序
②排序规则从大到小,排序算法为冒泡排序
③分别利用char数组和int数组进行测试
template<typename T>
void Swap(T &a,T &b)
{
T temp = a;
a = b;
b = temp;
}
template<class T>
void Sort(T arr[],int len)
{
for (int i = 0; i < len - 1;i++)
{
for (int j = 0; j < len - 1 - i;j++)
{
if(arr[j]>arr[j+1])
{
Swap(arr[j], arr[j + 1]);
}
}
}
}
template<typename T>
void printArray(T arr[],int len)
{
for (int i = 0; i < len;i++)
{
cout << arr[i] << endl;
}
cout << endl;
}
void text_char()
{
char charArr[] = "qwerasd";
int num = sizeof(charArr) / sizeof(char);
Sort(charArr, num);
printArray(charArr, num);
}
void text_int()
{
int intArr[] = {7, 4, 1, 8, 5, 0, 2, 9, 6, 3};
int num = sizeof(intArr) / sizeof(intArr[0]);
Sort(intArr, num);
printArray(intArr, num);
}
int main()
{
//text_char();
text_int();
system("pause");
return 0;
}
五、普通函数与函数模板调用规则
1、如果函数模板和普通模板都可以实现,优先调用普通函数
2、可以通过空模版参数列表来强制调用函数模板
3、函数模板也可以发生重载,此时调用函数模板
4、如果函数模板可以产生更好的匹配,优先调用函数模板
void myPrint(int a,int b)
{
cout << "调用普通函数" << endl;
}
template<typename T>
void myPrint(T a,T b)
{
cout << "调用模板" << endl;
}
template<typename T>
void myPrint(T a,T b,T c)
{
cout << "调用重载模板" << endl;
}
int main()
{
int a = 10;
int b = 20;
//如果函数模板和普通模板都可以实现,优先调用普通函数
myPrint(a, b);
//可以通过空模版参数列表来强制调用函数模板
myPrint<>(a, b);
//函数模板也可以发生重载,此时调用函数模板
int c = 30;
myPrint(a, b, c);
//如果函数模板可以产生更好的匹配,优先调用函数模板
char c1 = 'a';
char c2 = 'b';
myPrint(c1, c2);
system("pause");
return 0;
}
六、模板的局限性
模板的通用性并不是万能的
例如:
template<typename T>
void f(T a,T b)
{
a = b;
}
在上述代码中提供的幅值操作,如果传入的a和b是一个数组,就无法实现了。
template<class T>
void f(T a,T b)
{
if(a>b)
{
}
}
在上述代码中,如果T的数据类型传入的是Person这样的自定义数据类型,也无法正常运行。
因此,c++提供了模板的重载,可以为这些特定类型提供具体化的模板
示例:
class Person
{
public:
Person(string name,int age)
{
this->m_Name = name;
this->m_Age = age;
}
public:
string m_Name;
int m_Age;
};
//普通函数模板
template<class T>
bool myCompare(T& a,T&b)
{
if(a==b)
{
return true;
}
else
{
return false;
}
}
//具体化,显示具体化的原型和定意义以template<>开头,并通过名称来指出类型
//具体化优先于常规模板
template<> bool myCompare(Person &p1,Person &p2)
{
if(p1.m_Name==p2.m_Name&&p1.m_Age==p2.m_Age)
{
return true;
}
else
{
return false;
}
}
void text01()
{
int a = 10;
int b = 20;
bool ret = myCompare(a, b);
if(ret)
{
cout << "a=b" << endl;
}
else
{
cout << "a!=b"<<endl;
}
}
void text02()
{
Person p1("Tom", 10);
Person p2("Tom", 10);
bool ret = myCompare(p1, p2);
if(ret)
{
cout << "p1==p2" << endl;
}
else
{
cout << "p1!=p2" << endl;
}
}
int main()
{
//text01();
text02();
system("pause");
return 0;
}
七、类模板
作用:建立一个通用的类,类中成员的数据类型可以不具体制定,用一个虚拟的类型来代表。
template <class NameType,class AgeType>
class Person
{
public:
Person(NameType name,AgeType age)
{
this->name = name;
this->age = age;
}
void showPerson()
{
cout << "name:" << this->name << " age:" << this->age << endl;
}
public:
NameType name;
AgeType age;
};
void text01()
{
Person<string, int> P1("猴子", 99);
Person<int, float> P2(12, 1.11);
P1.showPerson();
P2.showPerson();
}
八、类模板与函数模板的区别
类模板没有自动类型推导的使用方式,且类模板在模板参数列表中可以用默认参数
void text01()
{
//Person p("猴子", 1000); //类模板使用的时候,不可以用自动类型推导
Person<string, int> p("孙悟空", 1000); //必须使用指定的类型方式
p.showPerson();
}
void text02()
{
Person<string> p("猪八戒", 99); //类模板中的模板参数列表,可以指定默认参数
p.showPerson();
}
九、类模板中成员函数创建时机
普通类中的成员函数一开始就可以创建
类模板中的成员函数在调用时才创建
十、类模板对象做函数参数
1、指定传入的类型:直接显示对象的数据类型
void printPerson1(Person<string,int> &p)
{
p.showPerson();
}
void text01()
{
Person<string, int> p("孙悟空", 100);
printPerson1(p);
}
2、参数模板化:将对象中的参数变为模板进行传递
template <class T1,class T2>
void printPerson2(Person<T1, T2>&p)
{
p.showPerson();
}
void text02()
{
Person <string, int> p("猪八戒", 200);
printPerson2(p);
}
3、整个类模板化:将这个对象类型模板化进行传递
template<class T>
void printPerson3(T &p)
{
p.showPerson();
}
void text03()
{
Person<string, int> p("唐僧", 300);
printPerson3(p);
}
十一、类模板与继承
1、当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
template<class T>
class Base
{
T m;
};
//class Son:public Base //错误,没有指出父类中T的类型
class Son:public Base<int> //必须指定一个类型
{
};
void text01()
{
Son s;
}
2、如果不指定,编译器无法给子类分配内存
3、如果想灵活指定出父类中T的内存,子类也需变为类模板
template<class T1,class T2>
class Son2:public Base<T2>
{
public:
Son2()
{
cout << typeid(T1).name() << endl;
cout << typeid(T2).name() << endl;
}
};
void text02()
{
Son2<int, char> child;
}
十二、类模板成员函数类外实现
//类模板中成员函数类外实现
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 << "姓名:" << this->m_name << " 年龄:" << this->m_age << endl;
}
void text01()
{
Person<string, int> p("abs", 10);
p.showPerson();
}
十三、类模板与友元
全局函数类内实现:直接在类内声明友元即可
全局函数类外实现:需要提前让编译器知道全局函数的存在
//2、全局函数配合友元 类外实现 先做函数模板声明,下方在做函数模板定义,在做友元
template <class T1, class T2>class Person;
template<class T1,class T2>
void printPerson2(Person<T1,T2> &p)
{
cout << "类外实现 姓名:" << p.m_name << " 年龄:" << p.m_age<<endl;
}
template<class T1,class T2>
class Person
{
friend void printPerson(Person <T1,T2> &p)
{
cout << "类内实现 姓名:" << p.m_name << " 年龄:" << p.m_age<<endl;
}
friend void printPerson2<>(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;
};
//1、全局函数在类内实现
void text01()
{
Person <string,int>p("Tom",20);
printPerson(p);
}
//2、全局函数在类外实现
void text02()
{
Person <string,int>p("Jerry",20);
printPerson2(p);
}