一、什么是泛型编程模板
在C++中,泛型编程模板是一种功能强大的工具,可以编写通用的代码,以处理多种不同类型的数据。使用泛型编程模板,可以实现函数模板和类模板。简单来说模板就像是生产汽车一样,同一款车型的所有车子外壳都出自同一模具。值得注意的是模板不能够直接使用,可以理解为一个框架,它能够提高复用性,将类型参数化。
二、函数模板
函数模板其作用是建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。基本语法为 template<typename Type>
其中: template 表示声明创建模板
typename 表示其后面的Type是一种数据类型,并且可以用class替代
Type 通用数据类型,通常首字母大写.
简单示例:
#include <iostream>
using namespace std;
template <typename T>
T add(T &a,T &b){
return a+b;
}
int main(){
int a = 2;
int b = 3;
cout << add<int>(a,b) << endl;
return 0;
}
2.1、具体化模板
在C++中,模板具体化(template specialization)允许你为特定类型参数提供自定义的实现,以覆盖模板的通用实现。通过模板具体化,可以针对特定类型或特定形参的组合编写特殊化的代码。上述示例称为部分特化。具体化模板也叫完全特化模板。在C++中提供了模板函数的重载,这样即使函数参数是自定义数据类型时也能够正常使用。
注意:具体化模板以template<>作为声明。在模板函数的具体化中,参数个数必须与通用模板函数的参数个数保持一致。
下面为一个示例:
#include <iostream>
using namespace std;
class Polygon{
public:
string my_name;
Polygon(string name):my_name(name){}
};
void MyArea(int edge){
cout << "普通函数被执行" << endl;
}
template<class T>
void MyArea(T &edge){
cout << "局部特化模板函数被执行" << endl;
}
template<> void MyArea(Polygon &p1){
cout << "具体化模板函数被执行" << endl;
}
int main(){
Polygon p1("多边形");
MyArea(4);
MyArea("abc");
MyArea<>(p1); //使用尖括号强制执行具体化模板函数
return 0;
}
通过运行结果可以看到当我们使用自定义的数据类型Polygon时,调用具体化模板函数。如果函数模板和普通函数都可以实现,优先调用普通函数 ,通过空模板参数列表来强制调用函数模板, 函数模板也可以发生重载, 如果函数模板可以产生更好的匹配,优先调用函数模板。为了避免二义性,使用函数模板就不使用普通函数。
三、类模板
类模板是一种C++编程语言中的特性,它允许定义通用的类。类模板可以在编译时根据传入的类型参数生成不同的具体类。建立一个通用类,类中的成员数据类型可以不具体指定,用一个虚拟的类型来代表。
#include <iostream>
#include <string>
using namespace std;
template <class TypeName,class TypeData>
class Polygon{
public:
TypeName my_name;
TypeData my_data;
public:
Polygon(TypeName name,TypeData data):my_name(name),my_data(data){}
};
int main(){
Polygon<string,float> p("圆",3.14);
cout << "名称:" << p.my_name << " 数据:" << p.my_data << endl;
return 0;
}
类模板即创建类的时候以虚拟数据类型TyName和TypeData先暂定,在实例化的时候再指定需要的数据类型。
3.1、类模板和函数模板的区别
主要区别就两点,函数模板中能够自动类型推导的使用方式,而类模板中无法这样使用,函数模板中在模板参数列表中不能够有默认值,而类模板中则可以。
模板参数列表中可以带默认值
在使用的时候就能够省略参数string和float了,但是类前面的“<>”无法省略。
3.2、类模板和普通类区别
类模板和普通类的创建循序不同:
1、普通类中的成员函数一开始就可以创建类模板中的成员函数在调用时才创建
3.3、类模板实例化对象做函数参数
当类模板实例对象作为函数的参数时,传参的方式共有三种,分别是:指定传参类型、将参数模板化、整个类进行模板化。
示例一(指定传参类型):
再实例化调用test函数即可
示例二(参数模板化):
可以看到这里的test2函数的参数里的Polygon类的两个数据类型均模板化,以虚拟的数据类型代替。调用时也直接传入实例对象即可,这里我全部用的<string,int>,另外typeid().nam()可用于获取参数的数据类型,需包含头文件#include <typeinfo>。
示例三(整个类模板化):
这里直接将polygon这个类模板进行模板化,类模板是指类中的成员未指定数据类型,而这里是将类的数据类型未指定,传入的p是<string,int>则类的数据类型就是<string,int>。
3.4、类模板继承
当类模板继承时需要注意:父类也是类模板时(即继承类模板)需要指定出父类中的数据类型,如果不指定,编译器则无法为子类分配内存,若需要更加灵活则将子类也变成类模板(即类模板继承类模板)。
下面是一个类模板继承类模板的示例:
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
template <class TypeName>
class ParentBase{
public:
TypeName my_name;
void PrintTest(){
cout << "这是一个父类" << endl;
cout << my_name << endl;
cout << typeid(my_name).name() << endl;
}
};
template <class T>
class MySon : public ParentBase<T>{
public:
MySon(T name){
this->my_name = name;
cout << "这是一个子类" << endl;
cout << typeid(name).name() << endl;
}
};
int main(){
MySon <int>myson(5);
myson.PrintTest();
return 0;
}