泛型编程就是独立于确定的类型的方式编写代码,需要提供具体程序实例所操作的类型或者值,而不是之前确定的。比如在stl容器中,vector<int>, vector<char> vector<double> 用的是不同的数据类型,但是声明和调用的语句却是一样的。这就是泛型--建立通用的模具以提高复用性。
泛型的一个关键就是模板。通过template关键字来声明使用模板,通过typename来定义模板使用的是哪种类型。基本的模板有两种:函数模板和类模板。
1. 函数模板:函数模板是一种特殊函数,可以通过不同类型进行调用。它是C++中的一种重要的代码复用形式。我们来看如下的代码:
//普通函数
void Swap(int& a, int& b)
{
int c = a;
a = b;
b = c;
}
//模板函数
template <typename T> //声明使用模板,并定义T是一个模板类型
void Swap(T& a, T& b) //紧接着使用T
{
T c = a;
a = b;
b = c;
}
int main(){
int a=0;
int b=1;
Swap(a,b); //自动调用,编译器根据a和b的类型来推导
float c=0;
float d=1;
Swap<float>(c,d); //显示调用,告诉编译器,调用的参数是float类型
Swap(c,d); //隐式调用
}
在调用的时候需要有实例化,在实例化中有显式实例化和隐式实例化。如上图,如果规定了类型就是显示实例化,没规定则是隐式实例化。那么问题就来了,如果有多个实例化的函数都可以满足本函数定义,那么用哪个呢?这也就是泛型编程的顺序问题:
- 先找参数完全匹配的普通函数(最开始首先寻找匹配的普通函数)。
- 再找参数完全匹配的模板函数。
- 再找实参经过自动类型转换后能够匹配的普通函数。
- 如果上面的都找不到,则报错
还有就是为什么函数模板能够执行不同的类型参数? 其实编译器对函数模板进行了两次编译
- 第一次编译时,首先去检查函数模板本身有没有语法错误
- 第二次编译时,会去找调用函数模板的代码,然后通过代码的真正参数,来生成真正的函数。
2. 类模板
同函数模板,类模板会建立一个通用类,类中的成员数据类型可以不具体制定,用typename类型来代表。比如说下面的模板类:
#include <string>
//类模板
template<class NameType, class AgeType>
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;
};
void test01()
{
// 指定NameType 为string类型,AgeType 为 int类型
Person<string, int>P1("孙悟空", 999);
P1.showPerson();
}
int main() {
test01();
system("pause");
return 0;
}
类模板和函数模板的区别如下:
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
比如下面的例子展现了类模板如果使用默认参数,以及不能使用自动推导:
#include <string>
//类模板
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;
}
类模板中成员函数和普通类中成员函数创建时机是有区别的:
- 普通类中的成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建,编译的时候不会
#include <iostream>
using namespace std;
class Person1
{
public:
void showPerson1()
{
cout << "Person1 show" << endl;
}
};
class Person2
{
public:
void showPerson2()
{
cout << "Person2 show" << endl;
}
};
template<class T>
class MyClass
{
public:
T obj;
//类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成
void fun1() { obj.showPerson1(); }
void fun2() { obj.showPerson2(); }//编译的时候编译器不会管有没有这两个函数,但是运行的时候可能会出错
};
void test01()
{
MyClass<Person1> m;
m.fun1();
m.fun2();//运行时会出错,说明函数调用才会去创建成员函数,编译的时候不会报错,但是正常情况下是没有showPerson2()函数的
}
int main() {
test01();
system("pause");
return 0;
}