模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。C++泛型编程和面向对象是两个分离的编程思维方式,完全可以独立学习。泛型编程经典实践是标准库STL,深入研读和编写STL库文件能够较好提高编程能力。当然模板的使用必然会导致代码的膨胀,但是这种膨胀对于代码简洁性是非常有益的,其产生的多余内存也是可以接受的。
模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector <int> 或 vector <string>。模板实际使用过程中分为类模板、函数模板和成员模板三中类型,下面一一对这三种类型模板概念进行说明。
1. 模板概念
在C++语言一开始为出现泛型编程概念,使用class作为模板的关键字,出现模板以后引入了typename。typename出现以后自定义类型内部的嵌套类型可以正常使用,同时也消除了类中同一个标识符可能导致的二义性。
1.1. 类模板
一些类主要用于存储和组织数据元素,类中的数据元素是与具体类型没有任何关系的,例如数组类、链表、queue等。C++将模板的思想应用于类,使得类实现不关注数据元素的具体类型,而只关注所需要的实现的功能。typename 是占位符类型名称,可以在类被实例化的时候进行指定,类型定义内部使用逗号分隔的列表来定义多个泛型数据类型。
template <typename type> class class-name {
...
}
template <typename type> m_var_type class-name::member = val;
template <typename type>
type class-name::func_member(...)
{}
类模板在工程中应用时,(1)必须在头文件中定义;(2)类模板不能分开实现在不同的文件中;(3)类模板外部定义的成员函数需要加上模板<>声明。
单例模板事例:单例模式 C++_生活需要深度-CSDN博客;
1.2. 函数模板
实际代码开发过程中对于重复出现的代码我们可以使用宏实现,也可以使用函数实现。但是这两种方式都有一定的限制。对于宏代码,可以很好的实现代码复用,使用所有类型,但是编译器不知道宏的存在,没有办法进行参数检查。对于函数,通过函数调用编译器可以进行类型检查,但是无法实现代码复用。C++函数泛型编程可以很好结合如上两者的优点。
template <typename type> ret-type func-name(parameter list)
{
...
}
函数模板重载实例
1.3. 成员模板
template <typename T>
struct xxx
{
public:
template <typename U>
xxx(const U& a) {
.......
}
typedef T first_type;
T value;
};
成员模板 智能指针:智能指针原理与使用 C++_生活需要深度-CSDN博客
2. 模板编译与耦合
2.1. 模板编译流程
编译器对于模板的处理方式是相同的,通过两次编译完成才能生成最终可执行文件。在预编译处理阶段通过具体类型推到出不同的类,在声明的地方对类模板代码本身进行编译,在使用的地方对参数替换后的代码进行编译。
2.2. 模板模板参数
在实际开发过程中可能出现模板套用模板的概念,也就是模板中包含了另外一个模板。
2.3. 多参数模板
函数模板可以定义任意多个不同的类型参数,和单参数相比较这里无法自动推导返回值类型,需要从左到右部分指定参数类型,指定的开始位置必定是函数的返回值参数。
#include <iostream>
#include <string>
using namespace std;
template
< typename T1, typename T2, typename T3 >
T1 Add(T2 a, T3 b)
{
return static_cast<T1>(a + b);
}
int main()
{
// T1 = int, T2 = double, T3 = double
int r1 = Add<int>(0.5, 0.8);
// T1 = double, T2 = float, T3 = double
double r2 = Add<double, float>(0.5, 0.8);
// T1 = float, T2 = float, T3 = float
float r3 = Add<float, float, float>(0.5, 0.8);
cout << "r1 = " << r1 << endl; // r1 = 1
cout << "r2 = " << r2 << endl; // r2 = 1.3
cout << "r3 = " << r3 << endl; // r3 = 1.3
return 0;
}