目录
快速理解模板
模板就是建立通用的模具,大大提高复用性:
例如,生活中的寸照模板或是PPT模板;
模板特点:
- 模板不可以直接使用,它只是一个框架(你的寸照必须要有你的头像和模板结合才是真正的寸照)
- 模板的通用并不是万能的(人的寸照模板不能用于宠物的)
泛型与模板
泛型程序设计(generic programming)是一种算法在实现时不指定具体要操作的数据的类型的程序设计方法。所谓“泛型”,指的是算法只要实现一遍,就能适用于多种数据类型。泛型程序设计方法的优势在于能够减少重复代码的编写。
泛型程序设计的概念最早出现于 1983 年的 Ada 语言,其最成功的应用就是 C++ 的标准模板库(STL)。也可以说,泛型程序设计就是大量编写模板、使用模板的程序设计。泛型程序设计在 C++ 中的重要性和带来的好处不亚于面向对象的特性。
在 C++ 中,模板分为函数模板和类模板两种。熟练的 C++ 程序员,在编写函数时都会考虑能否将其写成函数模板,编写类时都会考虑能否将其写成类模板,以便实现重用。
函数模板
函数模板的写法如下:
template <class 类型参数1, class类型参数2, ...>
返回值类型 模板名(形参表)
{
函数体
}
试一试:
#include <iostream>
using namespace std;
template<class T>
void Swap(T & x, T & y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int n = 1, m = 2;
//使用模板的两种方式:①自动推导型;②显示指定型
Swap(n, m); //编译器自动生成 void Swap (int &, int &)函数。这是自动推导性
double f = 1.2, g = 2.3;
Swap(f, g); //编译器自动生成 void Swap (double &, double &)函数
float x = 1.1, y = 2.2;
Swap<float>(x, y); //显示指定类型
return 0;
}
注意:编译器由模板自动生成函数的过程叫模板的实例化。由模板实例化而得到的函数称为模板函数。在某些编译器中,模板只有在被实例化时,编译器才会检查其语法正确性。如果程序中写了一个模板却没有用到,那么编译器不会报告这个模板中的语法错误。
函数模板调用顺序
函数模板可以重载,只要它们的形参表不同即可。
在有多个函数和函数模板名字相同的情况下,一条函数调用语句到底应该被匹配成对哪个函数或哪个模板的调用呢? C++编译器遵循的调用规则(顺序)如下:
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;
}
void test01()
{
//1、如果函数模板和普通函数都可以实现,优先调用普通函数
// 注意:如果告诉编译器普通函数是有的,但只是声明没有实现,或者不在当前文件内实现,就会报错找不到
int a = 10;
int b = 20;
myPrint(a, b); //调用普通函数
//2、可以通过空模板参数列表来强制调用函数模板
myPrint<>(a, b); //调用函数模板
//3、函数模板也可以发生重载
int c = 30;
myPrint(a, b, c); //调用重载的函数模板
//4、 如果函数模板可以产生更好的匹配,优先调用函数模板
char c1 = 'a';
char c2 = 'b';
myPrint(c1, c2); //调用函数模板
}
int main()
{
test01();
system("pause");
return 0;
}
普通函数与函数模板区别
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显示指定类型的方式,可以发生隐式类型转换
//普通函数
int myAdd01(int a, int b)
{
return a + b;
}
//函数模板
template<class T>
T myAdd02(T a, T b)
{
return a + b;
}
//使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换
void test01()
{
int a = 10;
int b = 20;
char c = 'c';
cout << myAdd01(a, c) << endl; //正确,将char类型的'c'隐式转换为int类型 'c' 对应 ASCII码 99
//myAdd02(a, c); // 报错,使用自动类型推导时,不会发生隐式类型转换
myAdd02<int>(a, c); //正确,如果用显示指定类型,可以发生隐式类型转换
}
类型一致
使用模板时,使用自动推导则必须所有类型都一致;否则不能直接使用模板函数。
//利用模板提供通用的交换函数
template<class T>
void mySwap(T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
// 1、自动类型推导,必须推导出一致的数据类型T,才可以使用
void test01()
{
int a = 10;
int b = 20;
char c = 'c';
mySwap(a, b); //正确,可推导出一致的T类型
//mySwap(a,c); 错误,推导不出一致的T类型
}
//2、模板必须要确定出T的数据类型,才可以使用
template<class T>
void func()
{
cout << "fun调用" << endl;
}
void test2()
{
//func(); 错误,模板不能独立使用,必须给出T的类型;因为这种没法根据参数推导!
func<int>(); //利用显示指定类型的方式,给T一个类型,才可以使用该模板
}
注:部分源代码和阐述出自“黑马程序员”相关学习文档!