C++中模板的应用(上)
模板简介
① C++另一种编程思想称为 泛型编程 ,主要利用的技术就是模板;
② C++提供两种模板机制:函数模板和类模板。
函数模板
函数模板的意义:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
函数模板-自动数据类型推导
#include<iostream>
using namespace std;
template<typename T1,typename T2>
void Compare(T1 a, T2 b)
{
if (a == b)
{
cout << "a==b" << endl;
}
}
int main()
{
int a = 10;
float b = 10;
Compare<int,float>(a, b);
}
函数模板-显式数据类型推导
#include<iostream>
using namespace std;
template<typename T1,typename T2>
void Compare(T1 a, T2 b)
{
if (a == b)
{
cout << "a==b" << endl;
}
}
int main()
{
int a = 10;
float b = 10;
Compare(a, b);
}
显式数据类型推导相较于隐式数据类型推导的优点
显示数据类型推导可以进行参数的强制数据类型转换
#include<iostream>
using namespace std;
template<typename T1,typename T2>
void Compare(T1 a, T2 b)
{
if (a == b)
{
cout << "a==b" << endl;
}
cout << typeid(a).name() << endl;
cout << typeid(b).name() << endl;
}
int main()
{
int a = 10;
double b = 10;
Compare<int,float>(a, b); // 传入数据类型为a-int,b-double,但是函数参数要求为a-int,b-float,因此发生强制类型转换
}
由于隐式数据类型推导只能根据传入形参的实际数据类型来实例化相应数据类型的实参,因此无法执行强制类型转换,即传入double类型的参数就是double类型的,无法再变为float类型的。
对于不能根据实参推导出数据类型的函数,隐式数据类型推导不可用
#include<iostream>
using namespace std;
template<typename T1,typename T2>
void showinf()
{
cout << "函数调用成功" << endl;
}
int main()
{
//showinf(); // 报错,无法推导出T1,T2数据类型是什么
showinf<int, int>();
}
普通函数与函数模板的调用顺序
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
int age;
string name;
Person(int age, string name)
{
this->age = age;
this->name = name;
}
};
void ShowInf(Person a, Person b)
{
if (a.age == b.age && a.name == b.name)
{
cout << "a==b" << endl;
}
cout << "调用普通函数" << endl;
}
template<typename T1,typename T2>
void ShowInf(T1 a, T2 b)
{
if (a == b)
{
cout << "a==b" << endl;
}
}
template<> void ShowInf(Person a, Person b)
{
if (a.age == b.age && a.name == b.name)
{
cout << "a==b" << endl;
}
cout << "调用函数模板的具体化" << endl;
}
int main()
{
Person a(18, "张三"), b(18, "张三");
ShowInf(a, b);
}
这里,我们看到有个函数可以实现“showing(a,b)”,分别为“具体化了的函数模板(相当于为了针对某一个数据类型而重载的函数模板)”和“普通函数”,但是编译器认为:调用函数模板还得实例化它,怪麻烦的,因此我就是用现成的普通函数吧,因此“在函数模板和普通函数均可以使用的前提下,普通函数优先级>函数模板优先级”。
#include <iostream>
using namespace std;
template<typename T1, typename T2>
void Compare(T1 a, T2 b)
{
if (a == b)
{
cout << "a==b" << endl;
}
cout << "调用函数模板" << endl;
}
void Compare(int a, float b)
{
if (a == b)
{
cout << "a==b" << endl;
}
cout << "调用普通函数" << endl;
}
int main()
{
int a = 10, b = 10;
Compare(a, b);
}
这里调用的是“函数模板”,这里有人问:为啥呢?前面那个不是优先调用的普通函数吗?我们要注意,前面的两个函数没有发生强制类型转换,但是我们这里普通成员函数形参类型是int,float,而我们传入参数是int,int,因此这里要发生一个强制类型转换的过程,反观函数模板的调用并不需要强制类型转换,因此编译器自然而然地调用最方便快捷那一个实现方式了。
在调用函数模板时才实例化函数模板
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
int age;
string name;
Person(int age, string name)
{
this->age = age;
this->name = name;
}
};
template<typename T1,typename T2>
void ShowInf(T1 a, T2 b)
{
if (a.age == b.age && a.name == b.name)
{
cout << "a==b" << endl;
}
}
int main()
{
Person a(18, "张三"), b(18, "张三");
ShowInf(a, b);
}
这里,函数模板中a,b都是T数据类型,T未知,但是我把a,b作为Person类对象来使用也是可以的,我用a,b来调用Person类对象的数据成员也是可行的,因为函数模板中一切都是一个模板,就相当于一个空架子,只有我们给予函数模板一个确定的数据类型(在调用函数时),此时函数模板才会被实例化。
此外,由于是调用时才创建,因此模板中出现错误(数据类型不正确引发的错误,比如:函数形参T在调用是应该是int,你却在函数体内出现了Object.MemberVar类成员的调用情况),是不会被提示的,只有当你实例化时,才可以动态的检查出错误。
普通函数与函数模板均可以实现时,如何指定用函数模板实现?
#include <iostream>
using namespace std;
void Compare(int a, int b)
{
if (a == b)
{
cout << "a==b" << endl;
}
cout << "调用普通函数" << endl;
}
template<typename T1,typename T2>
void Compare(T1 a, T2 b)
{
if (a == b)
{
cout << "a==b" << endl;
}
cout << "调用函数模板" << endl;
}
int main()
{
int a = 0, b = 0;
Compare<>(a, b); // 函数模板-添加一个空数据类型列表可以强制调用函数模板去实现该功能而非普通函数
Compare(a, b); // 普通函数
}
类模板
类模板与函数模板的异同点
类模板
① 类模板可以有默认模板参数;
② 类模板没有自动数据类型推导,只能通过显式数据类型推导来实例化对象;
③ 类模板无法具体化模板,即无法为某一中国特定情况来声明一个类模板。
函数模板
① 函数模板即可以进行自动(隐式)数据类型推导,也可以进行显式的数据类型推导;
② 函数模板可以是模板具体化,即针对于某一种特定情况来重载函数模板。
类模板的定义与实例化
程序示例
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2 = string> // class与typename作用一样,不起到声明类的作用
class Person
{
public:
T1 age;
T2 name;
Person(T1 age, T2 name)
{
this->age = age;
this->name = name;
cout << "age的数据类型为" << typeid(this->age).name() << endl;
cout << "name的数据类型为" << typeid(this->name).name() << endl;
cout << "Person类的实例化数据类型为" << typeid(Person).name() << endl;
}
void ShowInf()
{
cout << this->name << "的年龄为" << this->age << endl;
}
};
int main()
{
Person<int, string> Object1(18, "张三");
Object1.ShowInf();
}
结果显示
类模板作为函数参数
类模板作为函数形参的三种情况
① 指定传入的类型 --- 直接显示对象的数据类型;
② 参数模板化 --- 将对象中的参数变为模板进行传递;
③ 整个类模板化 --- 将这个对象类型模板化进行传递。
代码示例
#include <iostream>
#include <string>
using namespace std;
template<class T1, class T2 = string> // class与typename作用一样,不起到声明类的作用
class Person
{
public:
T1 age;
T2 name;
Person(T1 age, T2 name)
{
this->age = age;
this->name = name;
cout << "age的数据类型为" << typeid(this->age).name() << endl;
cout << "name的数据类型为" << typeid(this->name).name() << endl;
cout << "Person类的实例化数据类型为" << typeid(Person).name() << endl;
}
void ShowInf()
{
cout << this->name << "的年龄为" << this->age << endl;
}
};
void ShowInf1(Person<int, string> &p) // 普通函数
{
p.ShowInf();
}
template<typename T1, typename T2> // 函数模板嵌套类模板
void ShowInf2(Person<T1, T2> &p)
{
p.ShowInf();
}
template<typename T> // 函数模板嵌套类模板
void ShowInf3(T &p)
{
p.ShowInf();
}
int main()
{
Person<int, string> Object1(18, "张三");
ShowInf1(Object1);
ShowInf2(Object1); // 可以自动推导出来数据类型T1,T2,也可以用“ShowInf2<int,string>(Object1)”来实现相同的效果
ShowInf3(Object1); // 可以自动推导出来数据类型T,也可以用“ShowInf3<Person<int,string>>(Object1)”来实现相同的效果
}