C++入门泛型编程介绍
目录
- 函数模板
- 函数模板格式
- 函数模板的实例化
- 补充:
- 类模板
- 类模板的定义格式
- 类模板的实例化
- 非类型模板参数
- 模板特化
- 函数模板特化
- 类模板特化
- 全特化
- 偏特化
- 模板分离编译
- 模板总结
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
函数模板
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
函数模板格式
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
函数模板的实例化
隐式实例化:让编译器根据实参推演模板参数的实际类型
实现一个交换函数,在以前用c语言写的时候,针对不同的类型我们需要写多个交换函数,在C++中有了模板后并不需要,他会通过实际的参数来推断函数模板的参数类型,进而实例化出一份合适的函数供你使用
//函数模板
template <class T>
//模板参数T的类型并不是固定的,通过实际传递的参数来推导T的类型,实例化出一份函数
//template <typename T> 这种写法也是可以的
void Swap(T &num1, T& num2)
{T tmp = num1;num1 = num2;num2 = tmp;
}void functest()
{//交换整形int a = 10, b = 20;Swap(a,b);//隐士实例化cout << "a :" << a << "b :" << b << endl;//交换浮点型float c = 10.55f, d = 3.14f;Swap(c, d);//隐士实例化cout << "c :" << c << "d :" << d << endl;
}
流程示例图:
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
编译器根据实参的类型推模板参数的类型,这种也叫隐士类型转换,而显示实例化又是怎么样的呢
显式实例化:在函数名后的<>中指定模板参数的实际类型
//显示指定模板参数类型为int
int a = 10, b = 20;
Swap<int>(a, b);//显示实例化//显示指定模板参数类型为float
float c = 10.55f, d = 3.14f;
Swap<float>(c, d);//显示实例化
显示指定模板参数类型后,就不再需要编译器根据实参的类型去推断模板参数T的类型了,模板参数类型显示指定是啥就是啥
补充:
1、 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
2、 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
3、模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
类模板
类模板的定义格式
//可以声明多个模板参数
template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};
类模板的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
部分模拟实现vector类,后继在加以补充
//指定名命空间mzt
namespace mzt
{template<class T>class vector {public:vector() : _a(nullptr),_size(0),_capacity(0){ }//往容器中插入数据void push_back(const T& data){//扩容if (_capacity == _size) {size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;T* tmp = new T[newcapacity];assert(tmp);_a = tmp;_capacity = newcapacity;}_a[_size++] = data;}//返回pos位置处的值T& operator[](size_t pos) {assert(pos < _size);return _a[pos];}size_t getsize() {return _size;}private:T* _a;//size_t _size;//元素个数size_t _capacity;//空间};
}void func1()
{//使用显示指定实例化类对象mzt::vector<int> a;//插入数据a.push_back(1);a.push_back(2);a.push_back(3);a.push_back(4);//遍历for (size_t i = 0; i < a.getsize(); i++) {cout << " " << a[i];}cout << endl;
}
非类型模板参数
模板参数分类:类型形参、非类型形参。
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用
namespace mzt
{//模板参数给了缺省值后在定义对象时可以不指定显示模板参数template<class T = int, size_t N = 10> //声明模板参数和非类型模板参数class Array {public:void f() {//N = 10; //N为常量不允许修改}private:T* arr[N];};}
int main()
{mzt::Array< > a; //不提供模板参数,使用缺省值 return 0;
}
注意:
1、 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
2、 非类型的模板参数必须在编译期就能确认结果
模板特化
通常情况下使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化
函数模板特化
template<class T>
bool IsEqual(const T& left, const T& right)
{return left == right;
}int main()
{char arr1[] = "hello";char arr2[] = "hello";bool ret = mzt::IsEqual(arr1, arr2);// 0cout << ret << endl;const char* p1 = "hello";const char* p2 = "hello";ret = mzt::IsEqual(p1, p2); // 1cout << ret << endl;
}
同一字符串比较的结果确是不同,因为指针只会指向一块已经存在的空间,就是字符串的起始地址,而如果是数组的话他会创建两块不相同的空间,但是不符合预期的效果
解决办法模板特化
//方法一:
//这里针对字符串类型需要做特殊的处理
template< > //空括号
bool IsEqual<const char *>(const char* const& left, const char* const& right)
{return strcmp(left, right) == 0;
}//方法二:
//也可以采用以下写法,如果IsEqual这个函数有,编译器就不会去通过
//函数模板来实例化生成一份函数了
bool IsEqual(const char* left, const char* right)
{return strcmp(left, right) == 0;
}void func()
{char arr1[] = "hello";char arr2[] = "hello";bool ret = mzt::IsEqual(arr1, arr2);// 0cout << ret << endl;const char* p1 = "hello";const char* p2 = "hello";ret = mzt::IsEqual<const char*>(p1, p2);//1// 调用特化版本的函数模板的实例化,//当然这个模板参数类型也可以不显示指定,//1、如果不指定的话调用的就是方法二的函数了//2、指定参数类型后只会去调用特化版本的函数模板cout << ret << endl;}
函数模板特化总结:
1、必须要先有一个基础的函数模板
2、关键字template后面接一对空的尖括号<>
3、函数名后跟一对尖括号,尖括号中指定需要特化的类型
4、函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
//这里针对字符串类型需要做特殊的处理
template<>
bool IsEqual(const char* const& left, const char* const& right)
{return strcmp(left, right) == 0;
}
类模板特化
全特化
即是将模板参数列表中所有的参数都确定化。
namespace mzt
{template<class T1, class T2> class Array {public:Array() { cout << "<class T1, class T2>" << endl; }};template<>class Array<int,int>{public:Array() { cout << "<int,int>" << endl; }private:};template<>class Array<int, double>{public:Array() { cout << "<int,double>" << endl; }private:};template<>class Array<int*, int*>{public:Array() { cout << "<int*, int*>" << endl; }private:};template<>class Array<int&, int&>{public:Array() { cout << "<int& ,int&>" << endl; }private:};}int main()
{ //都实例化了特化版本的类模板,调用构造函数创建对象mzt::Array<int, int>a1;mzt::Array<int, double>a2;mzt::Array<int*, int*>a3;mzt::Array<int&, int&>a4;mzt::Array<double, int>a5;return 0;
}
偏特化
偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类
部分特化
namespace mzt
{template<class T1, class T2> class Array {public:Array() { cout << "<class T1, class T2>" << endl; }};//部分特化,将第二个参数特化为inttemplate<class T1>class Array<T1, int>{public:Array() { cout << "<class T1, int>" << endl; }};//部分特化,将第二个参数特化为doubletemplate<class T1>class Array<T1, double>{public:Array() { cout << "<class T1, double>" << endl; }};}int main()
{ mzt::Array<int,int> a1; mzt::Array<int, double> a2;mzt::Array<char, char> a3;//调用原类模板return 0;
}
参数更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
namespace mzt
{template<class T1, class T2> class Array {public:Array() { cout << "<class T1, class T2>" << endl; }};//指针类型的偏特化template<class T1, class T2>class Array<T1*, T2*>{public:Array() { cout << "<T1*, T2*>" << endl; }};//引用类型的偏特化template<class T1, class T2>class Array<T1&, T2&>{public:Array() { cout << "<T1&, T2&>" << endl; }};
}int main()
{ //根据显示指定的模板参数类型编译器自动去调用该类模板mzt::Array<int,int> a1;mzt::Array<int*, double*> a2;mzt::Array<char&, char&> a3;return 0;
}
模板分离编译
func1.cpp
template<class T>
T Sub(T& left, T& right)
{return left - right;
}
func1.h
template<class T>
T Sub(T& left, T& right);
test.c
int main()
{ int a = 10, b = 20;int ret = Sub(a,b);cout << ret << endl;return 0;
}
模板分离编译后在链接的过程中会发现找不到Sub函数
解决办法显示实例化
template<class T>
T Sub(T& left, T& right)
{return left - right;
}
//定义的时候显示实例化
template
int Sub<int>(int& left, int& right);
解决办法二:不需要显示实例化,函数模板的定义和声明都放到.h文件中
template<class T>
T Sub(T& left, T& right)
{return left - right;
}
模板总结
【优点】
- 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
- 增强了代码的灵活性
【缺陷】
- 模板会导致代码膨胀问题,也会导致编译时间变长
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误
3.模板不支持分离编译
C++入门泛型编程介绍相关推荐
- java开发和基于asp.net开发有什么优越性?_java语言的入门开始介绍
java编程语言是目前世界最流行的编程语言,它是在c++的基础上开发出来的语言,它取其精华去其糟粕让java语言具有功能强大和简单易用的特征. java具有:面对对象.分布式.健壮性.安全性.平台独立 ...
- FPGA入门基础介绍
1.1 FPGA入门基础介绍 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA入门基础介绍: 5)结束语. 1.1.2 本节引言 "不积跬步,无以至千里 ...
- EasyX入门和介绍
//EasyX入门和介绍 //画实心圆 #include <graphics.h> //引用EasyX图形库 #include <conio.h> int main() {in ...
- 【无标题】C#nbsp;语言入门基础介绍学习通http://www.bdgxy.com/
文章来源: 学习通http://www.bdgxy.com/ 普学网http://www.boxinghulanban.cn/ 智学网http://www.jaxp.net/ 表格制作excel教程h ...
- 第一章 51单片机开发入门知识介绍
第一章 单片机开发入门知识介绍 1.1 51单片机介绍 51单片机是对所有兼容Intel 8031指令系统的单片机的统称. 该系列单片机的始祖是Intel的8004单片机,后来随着Flash rom技 ...
- Android/Linux 子系统Graphics图形栈入门普法介绍
Android/Linux 子系统Graphics图形栈入门普法介绍 写在最前面 由于工作原因,最近在公司做了一个关于Android/Linux 子系统Graphics图形栈入门相关知识的 ...
- 51单片机入门基础介绍
51单片机入门基础介绍 文章目录 51单片机入门基础介绍 前言 常用元器件 发光二极管 数码管 LED数码管的显示方式 按键 定时计数器 串行口控制寄存器SCON 中断系统 中断源及中断请求 中断允许 ...
- Docker新手入门详细介绍
Docker新手入门详细介绍 Docker 起源于 dotCloud 公司的内部项目,于 2013 年 3 月以 Apache 2.0 授权协议开源,主要项目代码在 GitHub 上进行维护,2013 ...
- 新手入门:介绍JSP中request属性的用法
新手入门:介绍JSP中request属性的用法 一.request.getParameter() 和request.getAttribute() 区别 (1)request.getParameter( ...
最新文章
- Swift3.0语言教程查找字符集和子字符串
- 全局变量、静态全局变量、静态局部变量和局部变量的区别
- C# 3.X -- the newest features
- mysql 启动卡主,cpu 100%
- iOS内存管理部分内容
- Etcher/下载链接
- 2016西安教师职称计算机考试,2016教师职称计算机考试模块.doc
- lrzsz的交叉编译与使用
- mysql 内存 优化_MySQL核心参数优化(内存优化)
- 计算机网络复习笔记 之协议相关
- Win11系统启动文件夹是空的怎么解决?
- SAP CRM 如何高效debug 查看BOL的值
- 成功案例丨荣联助力中科院上海有机所冷冻电镜大数据计算系统建设
- 【Unity3D开发小游戏】《数字华容道》Unity开发教程
- mysql的配置中主机地址怎么填_连接到mysql主机数据库配置命令-mysql主机地址
- ContentTypes
- 摄影笔记一:《李元谈摄影修炼之道》
- 坐着打游戏也能减肥?1小时=200千卡,竞技游戏、解谜游戏效果更佳
- Java毕业设计-商城管理系统
- 本地git代码推送到远程git步骤