当前位置: 首页 > news >正文

C++:模板初阶

1.模板出现的背景

当我们写一个函数时,如果遇到函数接收的参数类型具有多样性,那么我们就需要重复地写多个函数,例如下面最简单的交换函数:

#include<iostream>
using namespace std;void Swap_char(char&a,char&b)
{cout << a << " " << b << endl;char tmp = a;a = b;b = tmp;cout << a << " " << b << endl;cout << endl;
}void Swap_int(int& a, int& b)
{cout << a << " " << b << endl;int tmp = a;a = b;b = tmp;cout << a << " " << b << endl;cout << endl;
}void Swap_double(double& a, double& b)
{cout << a << " " << b << endl;double tmp = a;a = b;b = tmp;cout << a << " " << b << endl;cout << endl;
}int main()
{char a = 'a', b = 'b';int c = 3, d = 6;double e = 3.0, f = 6.0;Swap_char(a,b);Swap_int(c,d);Swap_double(e,f);return 0;
}

一个简单的交换函数只是因为接收的参数有int,char,double三种类型就写出了三个结构类似的交换函数,在写代码时是非常忌讳类似这种重复代码的形式。因此C++的祖师爷就研究出来了“模板”这一种概念。

 

2.模板的概念

在C++中,模板是一种可重用的代码结构,用于生成其他代码或类。模板允许我们定义通用的函数或类,并根据需要自动创建具体的实例。

3.模板的分类

模板分为函数模板和类模板。

3.1函数模板

函数模板允许我们定义一个通用的函数,该函数可以接受不同类型的参数,并根据实际的参数类型进行类型推断来确定具体的函数实例。函数模板的定义通常以关键字“template”开头,后跟模板参数列表和函数体。

3.1.1单种数据类型

还是以刚刚所说的交换函数为例,将前面的交换函数改成模板形式:

#include<iostream>
using namespace std;template<class T>void Swap(T& a, T& b)
{cout << a << b << endl;T tmp=0;tmp = a;a = b;b = tmp;cout << a << b << endl;
}int main()
{int i = 1, j = 2;double m = 1.1, n = 2.2;char a = 'a', b = 'b';Swap(i,j);Swap(m, n);Swap(a, b);return 0;
}

运行结果如下:我们发现使用一个函数模板就可以将不同类型数据成功地交换。

代码中的“T”就代表一个通用的类型,在用户输入参数时可以推导出参数的类型并完成相应的指令。这个时候我们想一个问题:如果同时出现函数模板和一个确定的函数,那么编译器会如何选择呢?我们来看下面这段代码:

#include<iostream>
using namespace std;template<class T>void Swap(T& a, T& b)
{cout << a << b << endl;
}//void Swap(int& a, int& b)
//{
//	cout << a << b << endl;
//	int tmp = 0;
//	tmp = a;
//	a = b;
//	b = tmp;
//	cout << a << b << endl;
//}int main()
{int i = 1, j = 2;Swap(i,j);return 0;
}

注释Swap(参数是int&)的代码段时,起作用的相当于是函数模板;当取消下面代码段的注释时,此时出现了函数重载(函数名相同,但是参数不同),此时主函数调用Swap函数,起作用的就是(参数是int&)的Swap函数。原因在于函数模板相当于便于不知道参数具体类型时可以根据传入的参数自动推导,而如果有一个现成的确定了参数类型的同名函数,那么模板函数就可以“休息”了,不用发挥作用。从而导致取消注释前和取消注释后代码运行的结果不一样。

3.1.2多种数据类型

代码如下:

#include<iostream>
using namespace std;template<class T1,class T2>T2 Add(const T1& a,const T2& b)
{return a + b;
}int main()
{int i = 1;double j = 2.2;//推导实例化cout << Add(i, j) << endl;cout << Add(i, (int)j) << endl;//显示实例化cout << Add<int>(i, j) << endl;cout << Add<double>(i, j) << endl;//显示实例化return 0;
}

这段代码使用了Add函数的函数模板,因为相加的两个数有可能属于不同类型,所以需要T1和T2两个“模板变量”(自己取的名字) 。同时在引用函数的时候分为推导实例化和显示实例化:推导是根据导入参数的类型进行判断,可以利用(类型名)这种方式修改变量的类型,和强转有相似之处;显示实例化是直接使用<类型名>改变函数的返回值。

3.2类模板

类模板的大致思路相同,拿出我们之前用C语言实现过的栈来说,栈中存放的可能是int , char , double ...等多种类型的数据,为例防止重复地写相同的代码,我们继续使用前面所说的“T”来定义数组的类型,代码如下:

#include<iostream>
using namespace std;template<class T>class Stack
{
public:Stack(int n=4):_array(new T[n]),_size(0),_capacity(n){}~Stack(){delete[] _array;_array = nullptr;_size = _capacity = 0;}void PushStack(const T& a){if (_size == _capacity){T* tmp = new T[_capacity*2];memcpy(tmp,_array,_capacity*sizeof(T));_array = tmp;_capacity *= 2;}_array[_size] = a;_size++;}void PrintStack(){int i = 0;while (_size - i){cout << _array[i] << " ";i++;}}private:T* _array;size_t _size;size_t _capacity;};int main()
{Stack <int> s1;s1.PushStack(1);s1.PushStack(2);s1.PushStack(3);s1.PushStack(4);s1.PrintStack();cout << endl;Stack <char> s2;s2.PushStack('a');s2.PushStack('b');s2.PushStack('c');s2.PushStack('d');s2.PrintStack();cout << endl;return 0;
}

如上述代码所示,s1\s2是两个不同的栈(一个存int,一个存char),这个是C语言typedef做不到的,因为typedef终究还是只能存储一个类型,只是这个类型可以修改而已,但是不能同时存在。代码运行结果如下:

注意:类模板都是显示实例化。


感谢阅读,如有错误恳请批评指正 


http://www.mrgr.cn/news/44440.html

相关文章:

  • 国外电商系统开发-运维系统文件上传
  • java注解的处理器
  • 第6篇:三大渗透测试框架权限维持技术
  • 轻松部署大模型:Titan Takeoff入门指南
  • [CKA]十五、添加 Sidecar 容器并输出⽇志
  • Spring Boot驱动的现代医院管理系统
  • 怎么成为年薪53万的AI产品经理?我分析了200份大厂的招聘要求
  • js 字符串下划线转驼峰 驼峰转下划线
  • Polars的Functions
  • 一行代码轻松搞定!Sq.io让你的数据库查询像玩JSON一样简单
  • ChatGPT写论文全流程揭秘:从构思到成稿!
  • Python知识点:结合Python工具,如何使用TfidfVectorizer进行文本特征提取
  • MyMetaObjectHandler 没有进入,如何解决?
  • (21)Nakagami-m分布及其参数的意义
  • 【AIGC】ChatGPT是如何思考的:探索CoT思维链技术的奥秘
  • DTO(数据传输对象)
  • SSM社区慢性病管理系统—计算机毕业设计源码37572
  • 【AI大模型】深入Transformer架构:编码器部分的实现与解析(下)
  • SpringBoot项目内部配置文件加载顺序
  • 系统架构设计师教程 第14章 14.1 云原生架构产生背景 笔记