• 模板初阶学习


    ✨前言✨

    📘 博客主页:to Keep博客主页
    🙆欢迎关注,👍点赞,📝留言评论
    ⏳首发时间:2023年11月21日
    📨 博主码云地址:博主码云地址
    📕参考书籍:《C++ Primer》《C++编程规范》
    📢编程练习:牛客网+力扣网
    由于博主目前也是处于一个学习的状态,如有讲的不对的地方,请一定联系我予以改正!!!

    1 初识泛型编程

    我们之前学过一个交换的Swap函数,可以交换两个变量之间的值!但是我们后续在使用过程中就会发现,如果交换的两个变量的类型改变了,我们就必须要在写过一份!这样代码的复用率就太低了!那么我们在C++中为了解决这种代码复用率较低的问题。引用泛型编程这一概念!

    泛型编程:编写一个与类型无关的通用代码!让编译器自己进行识别!这种是代码复用常见的手段!模板就是泛型编程的基础!

    模板就可以简单理解为是通用的代码!以Swap函数为例:就是可以根据你传入的数据类型,编译器会生成对应类型的交换函数!模板可以分为:函数模板和类模板

    2 函数模板

    2.1 概念

    函数模板的概念:函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本!

    template<typename T,typename X……>
    返回值类型 函数名(参数列表){}
    
    • 1
    • 2

    template表示这是一个模板,typename关键字是用来定义模板参数的关键字!也可以使用class关键字。但是不能用struct来代替class关键字!则我们可以写出Swap函数的模板:

    template<typename T>
    
    void Swap(T& x,T& y)
    {
    	T tmp = x;
    	x = y;
    	y = tmp;
    }
    
    int main()
    {
    	int a = 10;
    	int b = 20;
    	Swap(a,b);
    	char x = '0';
    	char y = '9';
    	Swap(x, y);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2.2 原理

    看起来我们使用的是同一份代码,但实际上我们用的不是同一份代码!在编译器编译阶段,编译器会根据传入的实参类型来推演生成我们所需要类型的Swap函数,比如说我们传入的实参是int类型那么此时编译器就会将模板参数换成int,从而调用!

    2.3 函数模板的实例化

    函数模板的实例化分为隐式实例化和显式实例化

    上面我们所写的关于Swap函数,自己传入实参,由编译器自己进行识别推演的,我们称为隐式实例化!下面我们来介绍一下什么是显式实例化:

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

    我们先来看这样一段代码:

    template <typename P>
    int ADD(P x,P y)
    {
    	return x + y;
    }
    int main()
    {
    	int a = 10;
    	double b = 20.0;
    	int ret = ADD(a, b);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在编译的时候就会出现报错,发现如下问题:
    在这里插入图片描述这是因为在调用ADD函数时,编译器不知道是应该把int转换成double,还是把double转换成int!此时我们可以利用模板的显式实例化!

    int ret = ADD<int>(a, b);
    
    • 1

    指明要将double转换成int类型!

    2.4 模板参数的匹配原则

    1️⃣一个非模板函数可以与一个同名的函数模板可以一起存在,并且该函数模板还是可以实例化生成同名非模板函数

    int ADD(int a, int b)
    {
    	return a + b;
    }
    template <typename P>
    int ADD(P x,P y)
    {
    	return x + y;
    }
    int main()
    {
    	int a = 10;
    	int b = 20;
    	ADD(a, b);//调用同名的非模板函数
    	ADD<int>(10, 20);//编译器会调用特化的模板函数
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2️⃣如果条件都相同,对于非模板函数与模板函数,会优先调用非模板函数,而不会从模板函数中在生成一个实例出来!如果模板函数能够产生一个更好匹配的函数,那么就会优先调用模板!

    int ADD(int a, int b)
    {
    	return a + b;
    }
    template <typename P1,typename P2>
    int ADD(P1 x, P2 y)
    {
    	return x + y;
    }
    
    int main()
    {
    	int a = 10;
    	int b = 20;
    	double c = 20.0;
    	ADD(a, b);//优先调用非模板函数
    	ADD(a, c);//优先调用模板函数
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3️⃣模板函数不支持自动类型转换(隐式的类型转换,比如把double转换成int),普通函数支持自动类型转换!

    3 类模板

    格式类型:
    template
    class 类模板名
    {
    // 类内成员定义
    };

    之前我们在C语言中学过typedef关键字,我们只需要改变typedef后的数据类型,就可以更改类中所要存储的数据类型,代码如下:

    typedef int Date;
    
    class Stack {
    private:
    	Date* _a;
    	int _capacity;
    	int _top;
    public:
    	Stack(int capacity = 10){
    		_a = new Date[capacity];
    		_capacity = capacity;
    		_top = 0;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    但是,这也是有局限性的,因为如果需要多个栈并且多个栈中的存储的数据类型不一样,我们也要多写几分类似的代码!类模板就可以很好的解决这个问题!

    template <class T>
    class Stack
    {
    private:
    	T* _a;
    	int _capacity;
    	int _top;
    
    public:
    	Stack(int capacity = 4)
    	{
    		_a = new T[capacity];
    		_top = 0;
    		_capacity = capacity;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    类模板的实现原理和函数模板的原理是一样的,但是类模板是通过显示实例化,而不是让编译器自己进行推演!

    int main()
    {
    	Stack<int> st1;//放int的栈
    	Stack<double> st2;//放double的栈
    	Stack<char> st3;//放char类型的栈
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意:通过类模板实例化的类不是和普通类一样,普通类的类名就是类型,实例化的类就是类名<数据类型>才是类型!
    类模板中函数的声明与定义写法如下所示:

    template <class T>
    class Stack
    {
    private:
    	T* _a;
    	int _capacity;
    	int _top;
    
    public:
    	Stack(int capacity = 4);
    };
    
    template <class T>
    Stack<T>::Stack(int capacity = 4)//指定类型作用域
    {
    	_a = new T[capacity];
    	_capacity = capacity;
    	_top = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    [3D检测系列-PV-RCNN] PV-RCNN论文详解、PV-RCNN代码复现、包含官网PV-RCNN预训练权重及报错问题
    https证书免费申请
    vscode中使用eslint+prettier的配置
    Windows 钉钉多开 dingtalkRC版
    国民技术 N32G45x RTThread 定时器 串口接收不定长数据
    六、redis安装和配置
    Fiddler Everywhere 3.2.1 Crack
    每日OJ题_多源BFS①_力扣542. 01 矩阵(多源BFS解决最短路原理)
    maven配置jib-maven-plugin插件构建java应用docker镜像
    室内单目深度估计-3
  • 原文地址:https://blog.csdn.net/qq_57107826/article/details/134399467