首页 > 编程语言 >template—模板初阶(C++)

template—模板初阶(C++)

时间:2024-04-10 18:30:45浏览次数:24  
标签:tmp 初阶 函数 C++ 实例 Swap template 类型 模板

        本篇将会对 Cpp 中的模板进行一个简单的介绍(后序还关系模板进阶,对模板的内容进行更深入的讲解),其中包括模板的使用:函数模板、类模板,以及对于泛型编程的理解。其中的重点为函数模板,介绍了函数模板的原理、隐式实例化和显示实例化、还有模板参数的匹配规则。目录如下:

目录

1. 泛型编程

2. 函数模板

2.1 函数模板的格式

2.2 函数模板原理

2.3 函数模板的实例化

2.4 模板参数的匹配规则

3. 类模板

1. 泛型编程

        通常我们在实现一个 Swap 函数的时候,对于不同参数类型的 Swap 函数,我们需要写很多个重载函数来解决这个 Swap 函数的问题,但是当我们遇到一个新的形参类型的时候,我们有需要重新写一个新的重载函数,那么我们有没有其他的办法来解决该问题呢?

        我们可以使用模板,只提供一个函数,就可以以上问题,不在需要写很多的重载函数。模板的出现,开辟了泛型编程的道路 ---> 也就为之后的 STL 打下了基础。

        泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。(模板是泛型编程的基础)

        如下:

// 函数重载的 Swap 函数
void Swap(double& x, double& y) {
	double tmp = x;
	x = y;
	y = tmp;
}

void Swap(int& x, int& y) {
	int tmp = x;
	x = y;
	y = tmp;
}

void Swap(char& x, char& y) {
	char tmp = x;
	x = y;
	y = tmp;
}

// Swap 的模板代码
template <typename T>
void Swap(T& x, T& y) {
	T tmp = x;
	x = y;
	y = tmp;
}

2. 函数模板

        对于函数模板的使用,存在很多的细节,将会在下面较为详细的列举。

        首先是对于函数模板的概念:函数模板代表了一个函数家族,在使用时被参数化,根据实参类型产生函数的特定类型版本

2.1 函数模板的格式

        函数模板格式如下:

template<typename T1, typename T2, ..., typename Tn>
返回值类型 函数名(参数列表){}

        注:typename 是用来定义模板参数的关键字,也可以使用 class ,但不能使用 struct 作为定义模板参数的关键字。

        样例如下:

template <typename T1, typename T2>
void Swap(T1& x, T2& y) {
	T1 tmp = x;
	x = y;
	y = tmp;
}

2.2 函数模板原理

        函数模板本身是一个蓝图,本身并不是函数,是编译器用使用方式产生特定具体类型的函数模具。其实模板就是将本应该由我们做的重复的事情交给了编译器。

        在编译器编译阶段,对于模板函数的使用,编译器根据传入的实参类型来推演生成对应类型的函数以提供调用,如:当使用 double 类型使用函数模板时,编译器通过对实参类型的推演,将 T 确定为 double 类型,然后产生一份专门处理 double 类型的代码,对于内置类型同样如此。如下的反汇编:

        如上,我们在汇编层面可以看见,执行到需要运行的函数时,函数模板会自动的推演函数参数类型。推理过程如下:

2.3 函数模板的实例化

        当我们使用不同类型的参数使用函数模板的时候,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显示实例化,如下:

        对于模板参数而言,只存在一个 T,而这个时候,我们将 int 类型的 a1 和 double 类型的 d2 传入到函数之中去,函数模板无法推断我们需要使用的函数是 int 类型的还是 double 类型,所以编译器会报错。解决以上问题一共有两种办法,分别是隐式转化和显示转化,如下:

        如上,红框圈住的为隐式实例化:隐式实例化让编译器根据实参推演模板参数的实际类型,我们可以在传入的参数中将其强转,已到达实参的类型相同。

        蓝框圈住的为显式实例化:显式实例化在函数名后的 <> 中指定模板参数的实际类型,这样可以将传入的所有类型都转换为 <> 中的类型。

        注:对于显示实例化和隐式实例化而言,当函数中确实存在类型的强转:在强转的时候会产生一个临时变量,而对于该临时变量而言是一个不可以修改的值,所以我们需要使用 const 对其进行修饰,若不使用 const 对其进行修饰,那么就会报错,所以说,这样的显式实例化和隐式实例化就不能用在 Swap 函数中,因为 Swap 函数中的参数需要交换,为需要改变的值,其中产生的临时变量与其函数参数不匹配,就会报错。

2.4 模板参数的匹配规则

        对于模板参数的匹配存在以下原则:

        1. 一个非模板函数可以和一个同名的函数模板同时存在,而且函数模板也可以被实例化为这个非模板函数,如下:

        当调用的函数与非模板函数匹配的时候,编译器不要使用模板进行特化,直接调用存在的函数,但是我们也可以调用模板函数,只需要在函数名后的 <> 添加对应的类型即可,就算已经存在对应的函数,我们仍然可以调用其模板函数。

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

        简单来说,会优先匹配已经存在的非模板函数,若没有,则在匹配模板函数。

        3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。

3. 类模板

        对于类模板而言,类模板的定义格式如下:

template<typename T1, typename T2, ..., typename Tn>
class classname{
    // 类成员定义
};

        如一个栈模板:

template <class T>
class Stack {
public:
	Stack(int capacity = 4)
		:_a(new T[capacity])
		, _size(0)
		,_capacity(capacity)
	{}
	~Stack();

	void Push(T x){}
	void Pop() {}
	T GetTop(){}
private:
	T* _a;
	int _size;
	int _capacity;
};

// 当模板中函数放在类外定义时,需要加模板参数列表
template <class T>
Stack<T>::~Stack() {
	delete[] _a;
	_size = 0;
	_capacity = 0;
	_a = nullptr;
}

int main() {
	Stack<int> s1;
	Stack<double> s2;
	return 0;
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

        对于以上的类模板的使用,我们可以发现,类模板更便于我们使用不同自定义类型的类,如我们可以定义出 double 类型的 stack,也可以定义出 int 类型的 stack。当我们需要在类外对类函数进行定义的时候,我们需要按照一定的形式。

标签:tmp,初阶,函数,C++,实例,Swap,template,类型,模板
From: https://blog.csdn.net/m0_74830524/article/details/137527719

相关文章

  • 内存管理new and delete(C++)
        在本篇中,将会较为详细的介绍在Cpp中的两个新操作符new和delete,将会介绍其中的底层原理,以及这两个操作符的使用方法。其中还介绍了new/delete操作符使用的细节,还扩展了一些有关定位new表达式的知识点。最后总结了malloc/free与new/delete的区别。目......
  • UE中创建Actor添加组件初始化(UEC++个人学习笔记)
    在ue中创建actorc++类,在actor的.h文件中添加五个组件又由上到下的作用分别为:获取下SceneComponent,用于操作其Transform等相应接口。获取静态模型组件。获取盒子碰撞组件。获取粒子特效组件。获取音频组件。#include"Components/SceneComponent.h"#include"Components......
  • C++核心编程
    C++核心编程本阶段主要针对C++面向对象编程技术做详细讲解,探讨C++中的核心和精髓。1内存分区模型C++程序在执行时,将内存大方向划分为4个区域代码区:存放函数体的二进制代码,由操作系统进行管理的全局区:存放全局变量和静态变量以及常量栈区:由编译器自动分配释放,存放......
  • ROS中自定义全局算法规划器(c++)
     ros中编写一个全局路径规划器并集成为ros插件,加载到turtlebot3机器人平台上仿真验证参考资料:ROS中自定义全局规划器(上)_算法部署_哔哩哔哩_bilibili官网教程:navigation/Tutorials/WritingAGlobalPathPlannerAsPlugininROS-ROSWiki1.建立工作空间mkdir-pjps_......
  • C++_STL提供了六大组件
    STL提供了六大组件StandardTemplateLibrary容器:Containers各种数据结构,如vector,list,deque,set,mep等。容器是类模板。在声明容器变量时,可以指定容器将保存的元素的类型算法:各种常用的算法,提供了执行各种操作的方式,包括对容器内容执行初始化,排序,搜索和转换等操作,比如sort,s......
  • C++笔试面试题整理
    常见C++笔试面试题整理1.C和C++的区别C是面向过程的语言,是一个结构化的语言,考虑如何通过一个过程对输入进行处理得到输出;C++是面向对象的语言,主要特征是“封装、继承和多态”。封装隐藏了实现细节,使得代码模块化;派生类可以继承父类的数据和方法,扩展了已经存在的模块,实现......
  • C++的保护类型还能这么玩
    将一个类的析构函数定义为 protected保护类型:这个类就不能在外部被析构,被定义。只能在它的子类,或者它的友元类里面去定义。定义了保护类型的析构函数,它的声明周期在子类或者友元类里面自动管理。最主要理解它的限制,理解生命周期就好。 定义为保护类型的好处:将一个类......
  • C++初阶:6.string类
    string类string不属于STL,早于STL出现看文档C++非官网(建议用这个)C++官网文章目录string类一.为什么学习string类?1.C语言中的字符串2.两个面试题(暂不做讲解)二.标准库中的string类1.string类(了解)2.string类的常用接口说明(注意下面我只讲解最常用的接口)(1)......
  • C++类拷贝控制 深拷贝 浅拷贝
    参考博文:https://www.cnblogs.com/zhxmdefj/p/11579364拷贝构造函数,拷贝赋值运算符拷贝构造函数第一个参数是自身类类型引用,其他参数都有默认值的构造函数就是拷贝构造函数。classSales_data{public:   Sales_data();  //默认构造函数   Sales_data(const......
  • C++U4新-第06课-二分答案
    二分答案学习目标 先学习单调性,二分查找的单调性意思二分答案单调性 二分答案的思路  [【二分答案】-砍树] #include<iostream>usingnamespacestd;intmain(){intn,m;inttree[1000005];cin>>n>>m;for(inti=1;i<=n;i......