【C++】模板详解

📢博客主页:https://blog.csdn.net/2301_779549673
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述

文章目录

  • 🏳️‍🌈一、C++模板的基础概念
  • 🏳️‍🌈二、函数模板
  • 🏳️‍🌈三、类模板
  • 🏳️‍🌈四、模板的高级用法
  • 🏳️‍🌈五、模板的应用场景
  • 🏳️‍🌈六、C++模板的优缺点
  • 🏳️‍🌈七、C++模板常见错误及解决方法
  • 🏳️‍🌈八、模板链接错误
  • 👥总结


🏳️‍🌈一、C++模板的基础概念

函数模板
是 C++ 中一种强大的编程工具,它允许定义通用的函数,其参数和返回值的类型可以在使用时根据实际情况确定。函数模板的定义形式为template <typename 类型参数> 返回类型 函数名(参数列表) {函数体}。例如,template <typename T> T max(T a, T b) {return a > b? a : b;}定义了一个可以比较并返回两个同类型参数中较大值的函数模板。

函数模板实现代码通用性和可复用性的关键在于,它将类型作为参数,使得同一个函数模板可以处理多种不同的数据类型。在编译时,根据实际传入的参数类型,生成相应的具体函数代码。这样避免了为每种类型都单独编写相似的函数,提高了代码的复用性。

类模板
类模板则允许定义通用的类,其成员变量和成员函数的类型可以在实例化时指定。类模板的定义方式类似 template <typename 类型参数> class 类名 {类体} 。比如,template <typename T> class Stack {public: void push(T value); T pop(); private: T* data; int size; }; 定义了一个可以存储任意类型元素的栈类模板。

类模板通过将类型参数化,使得一个类模板可以适应不同的数据类型,从而实现代码的通用性和可复用性。在实际使用时,通过指定具体的类型来实例化类模板,生成特定类型的类对象。

总的来说,C++ 中的函数模板和类模板通过将类型参数化,大大提高了代码的灵活性、通用性和可复用性,使得开发者能够更高效地编写代码。

🏳️‍🌈二、函数模板

函数模板的语法包含模板声明、函数定义以及函数调用三个部分。
模板声明使用 template <typename 类型参数> 来指定函数可以处理的类型。例如,template <typename T> 表示声明了一个类型参数 T 。
函数定义部分,形如 返回类型 函数名(参数列表) { 函数体 } ,其中参数列表和函数体中的操作都基于声明的类型参数。比如,T sum(T a, T b) { return a + b; } 定义了一个求和的函数模板。
函数调用时,可以显式指定类型参数,如 sum(1, 2) ,也可以让编译器根据传入的实参自动推断类型,如 sum(1.5, 2.5) 。
下面通过一个查找数组最大值的函数模板示例来说明其使用方法。

template <typename T>
T findMax(T arr[], int size) {T max = arr[0];for (int i = 1; i < size; i++) {if (arr[i] > max) {max = arr[i];}}return max;
}

在使用时,如果是整数数组 int arr[] = {1, 5, 3, 7, 2}; ,可以这样调用 findMax<int>(arr, sizeof(arr) / sizeof(arr[0]))
对于浮点数数组float arr[] = {1.2, 3.4, 2.1, 5.6};,则调用 findMax<float>(arr, sizeof(arr) / sizeof(arr[0]))

函数模板能够大大提高代码的复用性和通用性,使得开发者能够更加高效、简洁地编写代码。

🏳️‍🌈三、类模板

类模板的定义使用template <typename 类型参数> class 类名 {类体}的形式。通过这种方式,我们为类的成员变量和成员函数的类型提供了通用性。例如,template <typename T> class MyTemplate { T data; public: void setData(T value) { data = value; } T getData() { return data; } }; 这里,MyTemplate类模板可以接受任何类型T作为成员变量data的类型,并且成员函数setData和getData也能处理这种通用类型。

类模板的实例化是将模板具体化的过程。例如,MyTemplate<int> myIntTemplate; 这里创建了一个MyTemplate类模板的int类型实例。在实例化时,编译器会根据指定的类型生成具体的类定义和相关代码。

在类模板中定义成员函数可以在类模板定义内部,也可以在外部。在类模板定义内部定义的成员函数会被隐式声明为内联函数。例如:

template <typename T> class MyTemplate {T data;public:void setData(T value) { data = value; } 
};

如果在类模板定义外部定义成员函数,需要使用template关键字并指定模板参数。例如:

template <typename T>
T MyTemplate<T>::getData() { return data; }

这样,我们可以根据实际需求灵活选择成员函数的定义位置,以实现类模板的丰富功能和高效性能。

🏳️‍🌈四、模板的高级用法

模板特化
模板特化是指针对特定类型或特定情况,为模板提供特殊的实现。其主要用途在于当通用的模板定义对于某些特定类型不适用或需要特殊处理时,可以通过特化来提供更优化、更准确的实现。
例如,对于一个比较函数模板 template <typename T> bool compare(T a, T b) { return a == b; } ,如果对于浮点数的比较不能简单地使用 == 操作符,我们可以进行特化: template <> bool compare<float>(float a, float b) { return std::abs(a - b) < 1e-6; }

再比如,对于一个存储数据的类模板,如果对于特定类型(如 const char* )需要不同的存储方式和操作方法,也可以进行特化。

通过这样的特化,我们能够为特定类型提供更符合其特性和需求的实现,提高程序的准确性和性能。

模板的默认参数
模板默认参数是在定义模板时为模板参数指定默认值。这在简化代码和提高代码的易用性方面具有重要作用。

比如,定义一个函数模板template <typename T, int size = 10> void fillArray(T arr[], int count) { for (int i = 0; i < count; i++) { arr[i] = T(); } },这里为参数 size 指定了默认值 10 。
在使用时,如果不需要指定特定的 size 值,就可以直接调用fillArray<int>(myArray, 5) ,此时 size 将采用默认值 10 。

又例如,定义一个类模板 template <typename T = int> class Container {... }; ,当实例化时,如果不指定类型参数,将默认使用 int 类型。

模板默认参数使得代码在很多常见情况下无需提供所有参数,减少了代码的冗余,提高了代码的可读性和编写效率。

🏳️‍🌈五、模板的应用场景

(一)泛型编程
在泛型编程中,模板使得代码能够处理不同类型的数据,而无需为每种类型单独编写逻辑。例如,使用模板可以创建通用的排序算法,无论是对整数数组、浮点数数组还是自定义类型的数组,都能进行有效的排序。
通过模板,可以将算法的逻辑与具体的数据类型分离,提高代码的可维护性和可扩展性。同时,泛型编程还能实现通用的数据结构,如通用的链表、栈和队列等。
(二)容器类
模板在容器类中发挥着关键作用。像 std::vector 、 std::list 和 std::map 等标准容器类都是通过模板实现的。
以 std::vector 为例,它能够存储各种类型的元素,并且根据需要动态调整存储空间。这使得开发者无需为每种数据类型单独实现一个容器类,大大提高了开发效率。
在自定义容器类时,模板同样重要。可以根据特定需求定制容器类的行为和特性,以满足不同的应用场景。
(三)算法库
在算法库中,模板为各种算法提供了通用性。例如,查找算法、排序算法等都可以通过模板实现对不同类型数据的操作。
通过模板,可以避免为每种数据类型重复编写相同的算法逻辑,同时也能根据不同类型的数据进行优化。
(四)提高代码灵活性和效率的方法
为了提高代码的灵活性和效率,在使用模板时,可以合理运用类型萃取、模板偏特化和模板元编程等技术。
类型萃取可以在编译时根据类型的属性进行决策,优化代码的执行路径。模板偏特化则为特定类型提供专门的实现,提高代码的针对性和性能。模板元编程能够在编译时进行计算和类型推导,生成高效的代码。

同时,注意模板的使用场景和避免过度使用模板导致编译时间过长、代码膨胀等问题,也是提高代码灵活性和效率的重要方面。

🏳️‍🌈六、C++模板的优缺点

优点

  • 代码复用:C++模板允许编写通用的代码片段,能够处理多种不同的数据类型,避免了为每种特定类型重复编写相似的代码,极大地提高了代码的复用性,减少了代码量和开发时间。
  • 类型安全:模板在编译时进行类型检查,确保了代码只能对有效的类型进行操作,避免了运行时的类型错误,增强了程序的稳定性和可靠性。

缺点

  • 编译时间长:模板在编译时需要实例化,对于复杂的模板结构或大量使用模板的项目,编译时间可能会显著增加,特别是在大型代码库中,这会严重影响开发效率。

  • 调试困难:由于模板在编译过程中进行了大量的处理和替换,使得调试过程变得复杂。调试工具可能难以准确地跟踪和展示模板相关代码的执行情况,增加了查找和解决问题的难度。

  • 代码可读性降低:模板的语法和结构相对复杂,尤其是在嵌套使用或与其他高级特性结合时,可能会使代码变得难以理解和阅读,给代码维护带来挑战。

  • 代码膨胀:模板可能会为每种使用的类型生成单独的代码,导致最终生成的可执行文件体积增大,占用更多的存储空间。

🏳️‍🌈七、C++模板常见错误及解决方法

  1. 常见错误类型
    类型错误是指模板实参的类型与模板形参的预期类型不匹配,例如将字符串传递给期望整数的模板参数。
    推断错误常发生在编译器无法准确推断模板实参的类型时,导致代码无法正确编译。
    语法错误涵盖模板定义或实例化中的语法不正确,像遗漏分号、括号不匹配等。
    语义错误则是模板代码在逻辑上不正确,例如对未初始化的变量进行访问操作。
  2. 诊断技巧
    利用编译器错误消息是诊断模板错误的首要方式。编译器通常会生成较为详细的错误消息,指明错误的大致位置和本质。例如,当出现类型不匹配时,会明确指出预期类型和实际传入类型的差异。
    启用调试标志如 -g 和 -gstl 编译标志能够生成有关模板实例化的调试信息,帮助开发者更好地理解模板的执行过程。
    调试器如 gdb 可以在模板实例化期间逐步执行代码,精准定位错误的根源。通过单步调试,查看变量的值和代码的执行流程,找出异常之处。
    静态分析工具如 Clang Static Analyzer 和 GCC -Wall 等能够检测到模板中的潜在错误,提前发现可能存在的问题,提高代码质量。
  3. 避免错误的提示
    为避免模板错误,首先要确保模板实参与模板形参的预期类型严格匹配。在编写模板代码时,应清晰地了解模板参数的要求,避免随意传递不相符的实参。
    提供显式的模板实参类型能够帮助编译器更准确地推断和处理,减少因类型推断不准确导致的错误。
    仔细检查模板定义的语法,遵循 C++的语法规范,特别是模板相关的特殊语法规则。
    使用静态分析工具定期对代码进行检测,及时发现并纠正潜在的错误。在代码编写过程中,遵循良好的编程习惯和设计原则,提高代码的可读性和可维护性。

🏳️‍🌈八、模板链接错误

  1. 链接错误的起源
    链接错误是在 C++编程中常见的错误,发生在编译过程的链接阶段。链接过程是将编译器生成的目标文件链接成可执行文件或库文件的最后一步。常见的链接错误原因包括:
    函数或变量的定义缺失:在一个文件中调用了某个函数或使用了某个变量,但该函数或变量的定义在其他文件中且未被正确包含或引用。
    模板实例化问题:模板在使用时需要进行实例化,如果未能正确实例化,如模板的定义和使用在不同的文件中且未遵循正确的规则,就会导致链接错误。
    重复定义:多个文件中对相同的函数或变量进行了定义,导致链接器无法确定使用哪一个。
  2. 模板与链接错误的关系
    模板实例化过程与链接错误密切相关。当使用
    模板时,编译器会在编译时根据具体的类型进行实例化。然而,如果模板的声明和定义分离,且在使用模板的地方没有正确地包含或处理模板的定义,就会导致链接错误。例如,在一个文件中声明了模板函数,而在另一个文件中进行实例化调用,但没有正确地包含模板函数的定义,编译器就无法在链接阶段找到对应的函数实现。
  3. 实例分析与解决策略
    以下是一个模板链接错误的示例:
// template.h
template<typename T>
void templateFunction(T value) {// 函数实现
}// main.cpp
#include "template.h"
int main() {templateFunction<int>(5);  // 调用模板函数return 0;
}

在上述示例中,如果 template.h 中只包含了模板函数的声明,而没有定义,就会产生链接错误。
解决模板链接错误的策略如下:

在头文件中定义模板:将模板函数或类的声明和定义都放在头文件中,这样在包含头文件的地方都能获取到完整的模板信息。 包含模板定义头文件:确保在使用模板的文件中正确包含包含模板定义的头文件。

显式模板实例化:对于特定的类型,在某个位置显式地进行模板实例化,以确保链接时能找到对应的实现。

例如,对于上述示例,可以将模板函数的定义放在 template.h 中,或者在一个单独的源文件中显式实例化 templateFunction 。

👥总结

C++模板作为C++语言的强大特性,具有不可忽视的重要性和显著的优势。

其重要性体现在,极大地提高了代码的复用性,减少了重复代码的编写;通过编译时的类型检查保障了代码的类型安全;为泛型编程、容器类、算法库等提供了坚实的基础,使代码更具通用性和可扩展性。


本篇博文对 C++模板 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/384621.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

域内攻击手法——AS-REP Roasting攻击和Kerberoasting攻击

一、AS-REP Roasting攻击 1、AS-REP Roasting攻击原理 AS-REP Roasting是一种对用户账户进行离线爆破的攻击方式。但是该攻击方式使用上比较受限&#xff0c;因为其需要用户账户设置不要求Kerberos 预身份验证选项&#xff0c;而该选项默认是没有勾选的。Kerberos 预身份验证…

基于jeecgboot-vue3的Flowable流程仿钉钉流程设计器-发送信息服务处理

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、因为仿钉钉设计器里发送消息处理是一个服务任务&#xff0c;所以要根据这个服务任务进行处理 2、这里目前只对消息进行处理&#xff0c;就是用websocket的发送方式 输入相应的内容&…

最新爆火的开源AI项目 | LivePortrait 本地安装教程

LivePortrait 本地部署教程&#xff0c;强大且开源的可控人像AI视频生成 1&#xff0c;准备工作&#xff0c;本地下载代码并准备环境&#xff0c;运行命令前需安装git 以下操作不要安装在C盘和容量较小的硬盘&#xff0c;可以找个大点的硬盘装哟 2&#xff0c;需要安装FFmp…

Java-- Stream流

感受stream流 代码 package demo1;import javax.naming.Name; import java.util.ArrayList; import java.util.Arrays; import java.util.List;public class StreamDemo1 {public static void main(String[] args) {ArrayList<String> list1 new ArrayList<>();l…

基于深度学习算法,支持再学习功能,不断提升系统精准度的智慧地产开源了。

智慧地产视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。通过计算机视觉和…

Java | Leetcode Java题解之第279题完全平方数

题目&#xff1a; 题解&#xff1a; class Solution {public int numSquares(int n) {if (isPerfectSquare(n)) {return 1;}if (checkAnswer4(n)) {return 4;}for (int i 1; i * i < n; i) {int j n - i * i;if (isPerfectSquare(j)) {return 2;}}return 3;}// 判断是否为…

Spring Security学习笔记(二)Spring Security认证和鉴权

前言&#xff1a;本系列博客基于Spring Boot 2.6.x依赖的Spring Security5.6.x版本 上一篇博客介绍了Spring Security的整体架构&#xff0c;本篇博客要讲的是Spring Security的认证和鉴权两个重要的机制。 UsernamePasswordAuthenticationFilter和BasicAuthenticationFilter是…

Idea 编译项目报错 java: java.lang.OutOfMemoryError:GC overhead limit exceeded

报错 java: java.lang.OutOfMemoryError: WrappedJavaFileObject[org.jetbrains.jps.javac.InputFileObjectpos13979: GC overhead limit exceeded解决 默认是700M&#xff0c;有的时候项目引入的依赖包比较大&#xff0c;可能超过了700M,需要扩大&#xff0c;根据实际情况设…

Dockerfile指令详解和Docker操作命令

1.容器的特点&#xff1a;1&#xff09;自包含&#xff08;包括应用程序及其运行环境&#xff09;&#xff1b;2&#xff09;可移植&#xff1b;3&#xff09;相互隔离&#xff1b;4&#xff09;轻量级。 2.docker成为容器的事实标准在于&#xff1a;1&#xff09;在运行环境上…

开局一个启动器:从零开始入坑ComfyUI

前几天刷某乎的时候看到了一位大佬写的好文&#xff0c;可图 IP-Adapter 模型已开源&#xff0c;更多玩法&#xff0c;更强生态&#xff01; - 知乎 (zhihu.com) 久闻ComfyUI大名&#xff0c;决定试一下。这次打算不走寻常路&#xff0c;不下载现成的一键包了&#xff0c;而是…

ESP32和mDNS学习

目录 mDNS的作用mDNS涉及到的标准文件组播地址IPv4 多播地址IPv6 多播地址预先定义好的组播地址 mDNS调试工具例程mDNS如何开发和使用注册服务查询服务 mDNS的作用 mDNS 是一种组播 UDP 服务&#xff0c;用来提供本地网络服务和主机发现。 你要和设备通信&#xff0c;需要记住…

【计算机网络】静态路由实验

一&#xff1a;实验目的 1&#xff1a;掌握通过静态路由方法实现网络的连通性。 二&#xff1a;实验仪器设备及软件 硬件&#xff1a;RCMS-C服务器、网线、Windows 2019/2003操作系统的计算机等。 软件&#xff1a;记事本、WireShark、Chrome浏览器等。 三&#xff1a;实验方…

Spark实时(二):StructuredStreaming编程模型

文章目录 StructuredStreaming编程模型 一、基础语义 二、事件时间和延迟数据 三、​​​​​​​容错语义 StructuredStreaming编程模型 一、基础语义 Structured Streaming处理实时数据思想是将实时数据看成一张没有边界的表,数据源源不断的追加到这张表中,这可以让我…

实时捕获数据库变更

1.CDC概述 CDC 的全称是 Change Data Capture &#xff0c;在广义的概念上&#xff0c;只要能捕获数据变更的技术&#xff0c;我们都可以称为 CDC 。我们目前通常描述的CDC 技术主要面向数据库的变更&#xff0c;是一种用于捕获数据库中数据变更的技术&#xff0c;CDC 技术应用…

web网站组成

web网站由四部分组成&#xff1a;浏览器 前端服务器 后端服务器 数据库服务器 流程&#xff1a; 1.浏览器输入网站后&#xff0c;向前端服务器发送请求&#xff0c;前端服务器响应&#xff0c;静态的数据给浏览器。 2.前端代码中script中有url,这个是向后台发送请求的网…

Windows下帆软BI(finebi)单机部署移植(Tomcat)攻略

一、基础环境 操作系统&#xff1a;Windows 10 64bit 帆软BI 版本&#xff1a;V9.0/V10.0 HTTP工具&#xff1a;Tomcat 外置数据库&#xff1a;Oracle 11g 实验内容&#xff1a;将已经部署好的帆软BI从一台电脑移植到另一台电脑 二、前期准备 1、做好外置数据库移植&…

结合创新!小波变换+注意力机制,实现100%分类准确率

小波变换是一种新的变换分析方法&#xff0c;它能有效提取信号的局部特征&#xff0c;但无法完全捕捉数据重要部分。为了解决这个问题&#xff0c;我们引入注意力机制&#xff0c;利用其强化关注重点的优势&#xff0c;将两者结合&#xff0c;做到更全面、深入地挖掘数据特征&a…

【初阶数据结构】9.二叉树(4)

文章目录 5.二叉树算法题5.1 单值二叉树5.2 相同的树5.3 另一棵树的子树5.4 二叉树遍历5.5 二叉树的构建及遍历 6.二叉树选择题 5.二叉树算法题 5.1 单值二叉树 点击链接做题 代码&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* …

昇思25天学习打卡营第22天|CycleGAN图像风格迁移互换

相关知识 CycleGAN 循环生成网络&#xff0c;实现了在没有配对示例的情况下将图像从源域X转换到目标域Y的方法&#xff0c;应用于域迁移&#xff0c;也就是图像风格迁移。上章介绍了可以完成图像翻译任务的Pix2Pix&#xff0c;但是Pix2Pix的数据必须是成对的。CycleGAN中只需…

杭州社保卡办理-农业银行版本

step 1、杭州滨江高新支行 被告知只能工作日办理&#xff08;由于工作时间冲突&#xff0c;办理不了&#xff09; 询问哪个支行可以办&#xff0c;回答说不知道&#xff0c;让我自己去问。银行服务态度较差。 step 2、杭州滨江江南支行 市民卡显示这家&#xff0c;周六可以…