前言
在 C++ 编程世界里,模板作为泛型编程的核心机制,就像一把钥匙,能打开 “代码复用” 的新维度。你是否曾为处理不同类型数据而重复编写相似代码?是否好奇 STL 容器为何能适配任意数据类型?模板初阶将为你揭晓答案 —— 它允许我们编写与类型无关的通用代码,让同一套逻辑轻松驾驭 int、double、自定义类等不同类型,从根本上提升代码的灵活性与可维护性。
本章节将从函数模板与类模板的基础语法出发,通过实例解析模板的核心特性:从函数模板的隐式 / 显式实例化,到类模板的类型参数化设计,逐步揭开泛型编程的神秘面纱。无论你是刚接触 C++ 的新手,还是希望系统梳理模板知识的开发者,这里都将成为你理解 “类型抽象” 编程思维的起点,为深入 STL 源码与模板进阶技术奠定基础。让我们一起走进模板的世界,感受 “一次编写,多处通用” 的编程魅力!
泛型编程
概念:编写与类型无关的通用代码,是代码复用的一种手段。
函数模板
概念:函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
格式:
template<typename T1, typename T2,......,typename Tn>
(当然,这里的T1 T2都能改成其他的名字,不影响的)
返回值类型 函数名(参数列表){}
(template的中文就是模板的意思)
注意:T1和T2等等都可以是不同类型的
eg:
template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
应用:比如像库函数里面的swap,引申:这个swap还能交换指针的地址
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
函数模板的实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。实例化之后叫做模板函数。模板参数实例化分为:隐式实例化和显式实例化。
1.隐式实例化:让编译器根据实参推演模板参数的实际类型
2.显式实例化:一般不能隐式实例化的就要显式实例化(比如类模板就必须使用显式实例化),当然,也可以故意显式实例化
语法:在函数名后的<>中指定模板参数的实际类型 eg:Add<int>(a, b); 必须使用显式实例化的场景举例: template<typename T> T* test(int n) { return new T[n]; //当然,用new的话,一般要进行捕获异常 } 与下面这种可以不用的情况区分 template<typename T> T test(T n) { return n; }//这种是不用的哈,这样传的话,编译器就知道T是啥了
模板参数的匹配原则
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
3.模板函数不允许自动类型转换(也就是隐式类型转换)–(这个时候就要显式实例化了),但普通函数可以进行自动类型转换
4.模板参数有两种:
a.类型参数:即类型参数化,将来实例化为具体的实际类型,有点像函数的形参,形参可以接受不同值的实参
b.非类型参数:在定义时给定了具体的类型,用该类型定义的为常量
类模板
类模板的格式:
template<class T1, class T2, ..., class Tn>//里面类型不一定要是class
class 类模板名
{
// 类内成员定义
};
1.类模板里面的函数的定义和声明不要放在不同文件里面
2.如果类模板中函数放在类外进行定义时,需要加模板参数列表
举例: template <class T> Vector<T>::Vector() {} 模板参数列表就是上面的eg:T,两个的话就是eg:<T1,T2>
类模板的实例化
注意:类模板实例化只能显式实例化
eg: Vector<int> s1;
类模板和普通类的区别:
1.普通类,类名和类型是一样的
2.类模板,类名和类型不一样
比如: 类名 Vector 类型 Vector<T> 类模板实例化之后才有类型,比如:Vector<int>
作业部分
下列关于模板的说法正确的是(C)
A.类模板与模板类所指的是同一概念
B.类模板的参数必须是虚拟类型的
C.类模板中的成员函数全是模板函数
原因:
B:类模板是一个类家族,模板类是通过类模板实例化的具体类
C:类模板的第一行template<class T, size_t N>是这样的也行