【C++】初阶模板

server/2025/3/25 21:40:17/

🦄个人主页:修修修也

🎏所属专栏:C++

⚙️操作环境:Visual Studio 2022


泛型编程

        模板是C++泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

        模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。

        每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector <int> 或 vector <string>

        我们可以使用模板来定义函数和类,接下来让我们一起来看看如何使用。


函数模板

        很多时候,我们在编程时会遇到这样的情形,如在通讯录程序中,我们想要实现两个联系人的信息互换,如我们要将张三和李四除了姓名之外的所有信息做交换:

        这个时候因为交换的数据类型并不相同,就需要我们编写很多Swap交换函数来完成这一功能,如:

        仔细观察可以发现,这三个Swap交换函数除了参数类型不同,其余的函数逻辑是一模一样的,那么有没有一种方法可以简化这种重复又烦琐的工作呢?

        答案是有的,接下来为大家介绍一下函数模板:


函数模板的定义

        模板函数定义的一般形式如下所示:

template <typename T> 
ret_type func_name(parameter list)
{// 函数主体
}

         其中,各个名称所代表的含义如下图:

        了解了模板函数定义的一般形式后,我们尝试套用一下前面提到的Swap的案例:

template <typename T>//此处typename也可换为class
void Swap(T& x, T& y)
{T temp = x;x = y;y = temp;
}

        再编写一个主函数程序测试一下函数模板的功能:

int main()
{int a = 3, b = 5;double c = 3.3, d = 5.5;char ch1 = 'e', ch2 = 'f';Swap(a, b);Swap(c, d);Swap(ch1, ch2);cout << a << " - " << b << endl;cout << c << " - " << d << endl;cout << ch1 << " - " << ch2 << endl;return 0;
}

        测试结果如下图,可以看到,函数模板成功完成了不同的类型数据的交换工作:


函数模板的实现原理

        对于函数模板,还有一个值得我们思考的问题:

        答案是否定的,它们调用的并不是同一个函数,这点可以通过查看汇编的方法来验证模板的底层实现:

        也就是说,函数调用的其实是函数模板生成的具体的函数.由模板生成具体函数的这一过程也被称为模板的实例化:

        在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此.


函数模板定义多个模板参数

        函数模板的模板参数也可以定义多个,如:

template <typename T1, typename T2>
void Print(T1& x, T2& y)
{cout << x << " - " << y << endl;
}int main()
{int a = 3;double b = 1.35;char ch = 'A';Print(a, b);Print(a, ch);Print(b, ch);return 0;
}

        测试运行结果如下:


函数模板的实例

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

隐式实例化

        让编译器根据实参推演模板参数的实际类型.

template<class T>
T Add(const T& x, const T& y)
{return x + y;
}int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;//正常隐式实例化调用Add(a1, a2);Add(d1, d2);//错误隐式实例化调用Add(a1, d1);/*该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅*/// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化Add(a1, (int)d1);return 0;
}

显式实例化

        在函数名后的<>中指定模板参数的实际类型.

template<class T>
T Add(const T& x, const T& y)
{return x + y;
}int main(void)
{int a = 10;double b = 20.0;// 显式实例化Add<int>(a, b);return 0;
}

        显示实例化的实际应用场景,如下代码:

template<typename T>
T* Alloc(int n)
{return new T[n];
}int main()
{double* pd = Alloc(10);return 0;
}

        因为模板无法根据参数隐式推导出返回值的类型,因此就只能通过显示实例化来完成这一功能:

template<typename T>
T* Alloc(int n)
{return new T[n];
}int main()
{double* pd = Alloc<double>(10);return 0;
}


类模板

        正如我们定义函数模板一样,我们也可以定义类模板。泛型类声明的一般形式如下所示:

template <class T1,class T2,...,class Tn> 
class class_name 
{//类的相关定义
}//类的实例化
class_name<T1,T2,...,Tn> object_name
 

        在这里,T 是占位符类型名称,可以在类被实例化的时候进行指定。可以使用一个逗号分隔的列表来定义多个泛型数据类型。

        类模板的主要用途是当我们想要在一个项目文件中创建可以存储不同数据类型的类,比如我们需要三个栈,一个存储整形数据,一个存储浮点型数据,一个存储字符型数据,这时候创建3个仅数据类型不同的栈类就很麻烦,因此我们可以选择使用类模板来完成这项工作.代码如下:

//类模板
template<class T>
class Stack
{
public:Stack(size_t capacity = 3){_array = new T[capacity];_capacity = capacity;_size = 0;}void Push(const T& data){//CheckCapacity();_array[_size] = data;_size++;}//其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}private:T* _array;int _capacity;int _size;
};int main()
{//存储整形数据的栈stiStack<int> sti();//存储单精度浮点型数据的栈stfStack<float> stf();//存储字符型数据的栈stcStack<char> stc();return 0;
}

        类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类.

// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

结语

希望这篇关于 C++模板 的博客能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

相关文章推荐

【C++】动态内存管理

【C++】标准库类型string

【C++】构建第一个C++类:Date类

【C++】类的六大默认成员函数及其特性(万字详解)

【C++】内联函数

【C++】函数重载

【C++】什么是类与对象?

【C++】缺省参数(默认参数)

【C++】命名空间

【C++】“Hello World!“


http://www.ppmy.cn/server/706.html

相关文章

Postman之安装

Postman工具之介绍与安装 Postman是什么&#xff1f;Postman有几种安装方式&#xff1f; Postman是什么&#xff1f; postman是一款http客户端的模拟器&#xff0c;它可以模拟发出各种各样的网络请求&#xff0c;用于接口测试。 Postman有几种安装方式&#xff1f; 两种&…

[疑难杂症2024-003]如何判断一张没有头信息的dcm图像,是否是压缩图像?

本文由Markdown语法编辑器编辑完成&#xff0e; 1. 前言: DCM格式&#xff0c;是医学图像领域里面的通用格式&#xff0e;DCM图像一般分为两大部分&#xff0c;一部分是TAG信息&#xff0c;一部分是像素. 而TAG信息&#xff0c;一般又会分为两部分&#xff0c;如下图所示, 是…

元宇宙VR虚拟线上展馆满足企业快速布展的需要

想要拥有一个VR线上虚拟展馆&#xff0c;展现您的城市风采或企业特色吗? 相比实体展馆搭建&#xff0c;VR线上虚拟展馆投入资金少&#xff0c;回报周期短&#xff0c;只需几个月的时间&#xff0c;您就能开始资金回笼。那么一个VR线上虚拟展馆多少钱呢? 深圳VR公司华锐视点基…

SpringBoot整合Mybatis实现从数据库中读取blob属性的图片在html页面中无法显示并且出现乱码实体类,如何解决?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

5GNR刷题

5G帧结构 5G NR帧结构的基本时间单位是( C ) A) subframe B) slot C) Tc D) symbol 5G无线帧长是多少ms&#xff08;B&#xff09; A) 5 B) 10 C) 20 D) 40 下面哪种子载波间隔是中国移动白皮书中规定必选(B ) A) 15KHz B) 30KHz C) 60KHz D) 120KHz 5G参数集包含哪…

程序员购车指南

哈喽大家好&#xff0c;我是咸鱼。 爱车可以说是大部分男人的天性&#xff0c;而我对汽车的热情却远不及对手表的钟爱&#xff08;痴迷劳力士&#xff09;。以至于我的朋友掏出车钥匙指着上面的苹果树标志跟我介绍奔驰 AMG 系列的强劲性能和马力时&#xff0c;我只能尽量假装自…

深度学习在三维点云处理与三维重建中的应用探索

目录 点云数据处理 数据清洗 数据降噪和简化 数据配准 特征提取 数据增强 数据组织 性能考量 PointNet PointNet 算法问题 改进方法 三维重建 重建算法 架构模块 流程步骤 标记说明 优点和挑战 点云数据处理 数据清洗 去噪&#xff1a;点云数据通常包含噪声…

Restful API接口规范(以Django为例)

Restful API接口规范(以Django为例) Restful API的接口架构风格中制定了一些规范&#xff0c;极大的简化了前后端对接的时间&#xff0c;以及增加了开发效率 安全性保证–使用https路径中带 api标识路径中带版本号数据即资源&#xff0c;通常使用名词操作请求方式决定操作资源…