C++:模板

一.初识泛型编程

不难看出以上代码存在的问题:

  1. ++重复冗余++ :针对 int 、 double 、 char 类型,交换逻辑完全一致,却要编写多份函数,增加代码量与维护成本,违背"避免重复"的编程原则 。

  2. ++扩展性差++ :若后续需支持新类型(如 string 等),必须再编写对应的重载函数,无法灵活适配新增类型,可维护性低 。

  3. ++未体现泛型优势++:未借助C++模板机制简化代码,仍依赖手动编写重载,没能发挥泛型编程提升代码复用性、通用性的价值 。

就以上问题C++,提出来泛型编程与模板的概念。

C++ 里的泛型编程,简单说就是 用"模板"当"万能模具" ,让一套代码能处理不同类型数据 。

你可以把模板想成做月饼的模具,不管要做豆沙馅、蛋黄馅月饼(对应不同数据类型,像 int 、 double ),用同一个模具(模板)就行,不用为每种馅重新做模具。

写代码时,用 template 定义模板,里面用 T (或其他名字)当"类型占位符" 。比如写交换函数,不用给 int 、 double 分别写,搞个模板函数,让编译器根据传入的数据,自动把 T 换成对应类型,生成专属代码。

这样做能 ++少写重复代码++ (一套逻辑适配所有类型),还能++保证类型安全++ (编译器会检查类型对不对),像 STL 里的 vector 、 map 这些容器,底层就靠泛型模板实现,不管存 int 还是 string ,都用同一套框架,特方便!

模板分为以下两类:++函数模板和类模板++

二.函数模板

1.函数模板定义

C++函数模板是泛型工具,用"模板参数"适配多种类型,一套逻辑处理不同数据,省重复代码。

以上 C++ 函数模板定义。用 template<typename T> 声明, T 是类型参数,代表任意类型。定义 Swap 函数时,用 T 适配不同类型,调用时编译器依据实参自动推导 T 具体类型,生成对应函数,实现一套代码处理多种类型交换,体现泛型编程复用性。

(注:处理使用typename关机自定义模板参数,还可以用class定义模板参数,但不能用struct定义)

2.函数模板的原理

函数模板相当于一个模具,本身并不是函数。

以上是 C++ 函数模板的类型推导与实例化流程示意:

  1. 调用触发推导: main 里调用 Swap(d1,d2) ( double 类型)、 Swap(i1,i2) ( int 类型)、 Swap(a,b) ( char 类型)时,编译器会根据实参类型推导 T 。

  2. 生成专属函数:推导后,编译器为每种类型自动生成对应函数(如 double 版 Swap 用 double 替换 T , int 版同理 ),实现"一套模板、多份实例",既复用逻辑,又保证类型适配,体现泛型编程的灵活与高效。

3.函数模板的实例化

用不同类型参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:++隐式实例化和显式实例化 。++

(1)隐式实例化:让编译器根据实参推演模板参数的实际类型

以上这段代码完美体现了 C++ 函数模板的隐式实例化,核心流程可以拆成三步理解:

  1. 模板是"蓝图": template<typename T> void Swap(T& a, T& b) 是模板, T 是"类型占位符",本身不是真正能运行的函数,更像一套"逻辑蓝图"。

  2. 调用触发推导: main 里调用 Swap(a, b) 时,传入的 a 和 b 是 int 类型。编译器会自动推导:"哦,这里的 T 应该是 int "

  3. 隐式生成函数:编译器悄悄帮你干了件大事 ------ 根据推导结果,生成专门处理 int 类型的 Swap 函数(相当于自动写了 void Swap(int& a, int& b) { ... } )。这个过程完全"隐式",你不用手动写 Swap<int>(a, b) ,编译器默默把模板变成了能运行的具体函数。

简单说,隐式++实例化就是编译器根据你传的参数类型,自动把模板"变成"对应类型的实际函数++,让你写代码时不用操心类型匹配,既复用逻辑,又写得轻松。

(2)显式实例化

显式实例化:在函数名后的<>中指定模板参数的实际类型

以上这段代码里的 Swap<int>(a, b) 、 Swap<double>(x, y) 就是 显式实例化,关键看这两点:

  1. 手动指定类型:你在调用时,通过 <int> 、 <double> 直接告诉编译器:"用 int / double 替换模板里的 T !" 不像隐式实例化那样靠参数推导,这里是你主动指定类型。

  2. 强制生成实例:编译器收到指令后,会专门为 int 和 double 类型生成对应的 Swap 函数(比如 Sw1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这

简单说,显式实例化就是你直接指定模板参数类型,让编译器精准生成对应版本的函数,适合需要明确控制类型,或提前生成特定实例的场景。

4.函数模板的匹配规则

(1) 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这非模板函数。

展示了 C++ 函数模板匹配规则的典型场景。当调用 Swap(a, b) 时,因存在普通 int 版 Swap 函数,优先匹配它;调用 Swap(x, y) 时,无对应普通函数,触发模板隐式实例化生成 double 版。体现普通函数优先、模板按需实例化的匹配逻辑,帮我们理解函数模板调用时的选择机制 。

(2)对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而 不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

代码呈现 C++ 函数匹配的场景。存在模板 Add 与普通 int 版 Add ,调用时: Add(a,b) 因普通函数精准匹配优先调用; Add(a,x) 、 Add(x,y) 触发模板,编译器根据实参推导类型,实例化对应模板函数,体现函数匹配规则里普通函数优先、模板按需实例化的逻辑 。

三.类模板

1.类模板的定义格式

以下从类模板定义格式与显式实例化角度,对代码进行讲解:

①类模板定义格式

代码中 template<class T> class stack 遵循 C++ 类模板标准定义规范。

  • template<class T> :作为模板参数声明, class (也可替换为 typename )用于引入类型参数 T , T 本质是"类型占位符",可代表 int 、 double 等任意数据类型,为类适配不同数据场景提供基础 。

  • class stack :基于 T 构建类的整体结构,类内私有成员(如指针 T* _arr 、整型 _top 和 _capacity )、公有成员(构造函数 stack 、成员函数 push 声明 ),均借助 T 实现类型适配,使 stack 类能依据 T 的具体类型,处理对应数据的栈操作逻辑。

②显式实例化

在 main 函数中, stack<int> st1; 与 stack<double> st2; 属于类模板的显式实例化操作:

  • 实例化时,需手动通过 <int> 、 <double> 明确指定类型参数,向编译器传递指令:使用 int 、 double 类型替换类模板中 T ,生成专门处理对应类型数据的栈类(即 stack<int> 用于存储与操作 int 类型数据, stack<double> 用于 double 类型 )。

  • 类模板特性决定其无隐式实例化机制,必须由开发者显式指定类型,编译器才会依据模板定义,生成对应类型的具体类代码,以支撑程序运行。

简言之,类模板依托 template<class T> 搭建通用逻辑框架,使用阶段需通过显式指定类型(如 <int> 、 <double> ) 完成实例化,生成适配特定数据类型的类,达成一套模板复用、适配多类型场景的效果 。

(注:代码中构造函数内 _arr 虽经 new T[capacity] 动态分配内存,但未完善内存释放等资源管理逻辑,实际应用易引发内存泄漏等问题,需补充析构函数等进行优化 。 )

相关推荐
小汉堡编程1 小时前
数据结构——vector数组c++(超详细)
数据结构·c++
DKPT5 小时前
Java桥接模式实现方式与测试方法
java·笔记·学习·设计模式·桥接模式
tan180°6 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
好好研究7 小时前
学习栈和队列的插入和删除操作
数据结构·学习
彭祥.8 小时前
Jetson边缘计算主板:Ubuntu 环境配置 CUDA 与 cudNN 推理环境 + OpenCV 与 C++ 进行目标分类
c++·opencv·分类
lzb_kkk8 小时前
【C++】C++四种类型转换操作符详解
开发语言·c++·windows·1024程序员节
新中地GIS开发老师8 小时前
新发布:26考研院校和专业大纲
学习·考研·arcgis·大学生·遥感·gis开发·地理信息科学
SH11HF9 小时前
小菜狗的云计算之旅,学习了解rsync+sersync实现数据实时同步(详细操作步骤)
学习·云计算
Frank学习路上9 小时前
【IOS】XCode创建firstapp并运行(成为IOS开发者)
开发语言·学习·ios·cocoa·xcode
胖大和尚10 小时前
clang 编译器怎么查看在编译过程中做了哪些优化
c++·clang