静态多态之泛型编程(模板)

起初,我们写不同类型的加法函数是这样写的吧:

//Template.h

#pragma onceint Add(const int left,const int right)
{return left+right;
}char Add(const char left,const char right)
{return left+right;
}float Add(const float left,const float right)
{return left+right;
}</span>
//Template.cpp

int main()
{cout<<Add(1,2)<<endl;cout<<Add('1','2')<<endl;cout<<Add(1.1f,2.2f)<<endl;return 0;
}

运行结果:

是不是特别熟悉呢。对,这就是我们的函数重载。之前为了适应不同类型的参数,我们一直使用的函数重载的方式。但是这种方式有一定的局限性和缺点,比如:

(1)只要有新类型出现,就得新添加函数。

(2)重复的代码过多,代码复用率不高。

(3)如果只是函数返回值不同,就不能使用函数重载。

(4)一个方法有问题,所有的方法都有问题,不好维护。

那么,我们还知道什么方法可以解决这些问题呢?

肯定有人会说,使用宏定义的方法。

#define Add(a,b) ((a)+(b))
运行结果:



显然,上述两种方式打印出来的结果竟然不相同。这是为什么呢?

首先,用宏定义的方式没有类型检测,所以致使第二次打印char类型的值时打印成了int型,这样也使得代码安全性不高。

其次,宏不是函数,不能调试,如果出错的话,也会很麻烦。

这里,就给大家引入一个新的概念吧~【模板

模板是泛型编程的基础,那么,泛型编程指什么呢,就是编写与类型无关的逻辑代码。

模板分为函数模板和类模板,今天给大家讲一下函数模板。

一、函数模板

模板的格式:

template<typename T1,typename T2 ...>  【说明:typename是模板参数关键字,T1,T2是类型名】
那么,对于上述的加法问题,我们就可以写成这样的:

//Template.h

template<typename T>
T Add(const T& left,const T& right)
{return left+right;
}

//Template.cpp

int main()
{cout<<Add(1,2)<<endl;cout<<Add('1','2')<<endl;cout<<Add(1.1f,2.2f)<<endl;return 0;
}</span>

运行结果:

这样的结果也是和函数重载方式的结果是一样的。

我们可以想到,模板函数的类型是T,而传入的参数是固定的类型,它是怎么检测到函数的类型的呢。这里就有模板函数的推演过程

查看反汇编,当调用第一个Add(1,2)时,




当调用Add('1','2')时:


当调用Add(1.1,2.2)时:


其实背后的推演为这样的(编译器会自动的生成相应的函数):




二、模板参数

函数模板有两种类型参数:模板参数和调用参数。

模板形参分为类型形参和非类型形参。

(1)模板形参的名字只能在模板形参之后到模板声明或定义之间使用。


(2)模板形参的名字在同一模板形参列表中只能使用一次。


(3)所有模板形参前面必须加class或typename关键字。


(4)不能将模板形参作为函数实参。


还有:


(5)函数形参为离它最近的模板形参。

(6)默认的模板参数(在这里是不能使用的,只有在类模板中可使用)


三、非模板类型参数

例:对于数组长度来说,可以这样定义模板参数:


通过反汇编可以看到内部的实现:


还有另外一种调用方式是:


注:这里的数组arr的类型是int[10],所以T代表的是int[10]哦。

总结一下,模板形参需要注意的点:

(1)模板形参表需使用<>;

(2)模板形参中参数需用逗号隔开;

(3)模板形参表不能为空;

如:


(4)模板形参可以是类型参数(跟在class/typename后),也可以是非类型参数。

(5)模板形参可作为类型说明符用在模板的任何地方,与内置类型或自定义类型使用方法相同,可用于指定函数形参类型、返回值、局部变量、强制类型转换。

(6)模板形参表中,class和typename有相同的含义,但typename更加直观,一般多用typename。(注:旧版本可能不支持typename,只支持class)

四、模板函数的特化

为什么会引入模板函数的特化版本呢?

有时候,我们并不能写出对所有可能被实例化的类型都最合适的模板,此时,我们就会用特化的形式,来实现模板函数。

例:

//Template.h

template<typename T>
T Add(const T& left,const T& right)
{return left+right;
}template<>     //特化版本
int Add<int>(const int& left,const int& right)    
{return left+right;
}
//Template.cpp

int main()
{cout<<Add(1,2)<<endl;return 0;
}

此时,Add(1,2)函数会调用哪个模板函数呢?



这里要知道,如果是特化版本中有实参类型,就直接调用特化版本。好处是:不用进行模板参数的类型推演过程。

下面再看一个例子:

//Template.h

#pragma once
#include<string.h>template<typename T>
T Max(const T left,const T right)
{return (left > right) ? left:right;
}template<>
char* Max<char*>(char* const str1,char* const str2)
{if(strcmp(str1,str2) == 0 || strcmp(str1,str2) == 1){return str1;}else{return str2;}
}
//Template.cpp

#include"Template.h"
#include<iostream>
using namespace std;
int main()
{char* str1 = "zello";char* str2 = "world";cout<<Max<char*>(str1,str2)<<endl;return 0;
}
运行结果:



注:在这里要特别注意的是const的位置,它修饰的是字符串,而不是char*哦。



模板就先说到这里啦微笑微笑~~

下一篇博客,会写到模板类的实现哦。欢迎大家来访喽,并给出好的建议和意见哦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/382814.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站视频解析 有的url资源放在浏览器能直接播放,有的却不行。

1有的视频url放在浏览器地址栏&#xff0c;回车能直接播放 2.有的视频url却直接下载下来一个 很短暂的m3u8文件&#xff0c; 且不能播放 这时候把视频url放在 vlc 或者专门解析m3u8的网站上却能直接播放&#xff1a; 例如&#xff1a;https://youku.com-l-youku.com/20190207/2…

【数据结构】布隆过滤器原理详解及其代码实现

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…

c++详解【继承】

学过c的人都知道&#xff0c;c的三大特性&#xff1a;封装、继承、多态。 我们今天说的是c的继承&#xff0c;那么为什么要引入继承&#xff0c;它有什么特点呢&#xff1f; 首先&#xff0c;继承的特点是&#xff1a;使代码复用&#xff0c;为后面学习多态做铺垫。 继承分为…

centOS6.5如何从启动界面直接进入命令行界面和如何从图形界面进入命令行界面

centOS6.5如何从启动界面直接进入命令行界面 编辑 /etc/inittab 将 id:5:initdefault: 修改为 id:3:initdefault: 下次重启就不启动X Window了 如何从图形界面进入命令行界面 startx

优酷解析 转载的

转自 https://blog.csdn.net/qq_39797956/article/details/88076404

【送给Git初学者】

好多人都听过Git吧&#xff0c;目前最流行的分布式版本管理系统。还有好多类似的cvs、svn&#xff08;速度慢、必须联网&#xff0c;这些是集中式版本控制系统&#xff09;..... 那么&#xff0c;它是用来干什么的呢&#xff1f;举个例子可能更好理解吧&#xff01; 比如你写…

虚拟机中的Linux安装VMware Tools的方法

虚拟机中的Linux安装VMware Tools的方法 http://www.jb51.net/softjc/189144.html 当.pl文件无法执行时 chmod install-vmware.pl./ install-vmware.pl 安装就可。 先以root身份登入。 VMware Tools所在位置&#xff1a;VMware 安装路径 \VMware\VMware Workstation\linux…

appium 设置参数

appium 配置好环境变量以后&#xff0c; 需要设置启动参数&#xff0c; 设备名称&#xff0c; 应用的一些信息主要有以下信息&#xff1a; {"platformName": "Android","platformVersion": "5.1.1","deviceName": "ee…

远程仓库

上节我们安装好了git&#xff0c;并配置好git&#xff0c;github之间的ssh。这节我们就开始用git管理我们的仓库吧。&#xff08;这节在windows下安装的git bash上给大家演示吧&#xff09; 首先&#xff0c;创建好一个仓库&#xff0c;主要步骤如下&#xff1a; 创建好仓库后…

linux根目录的意义和内容

1.du命令&#xff1a;du [选项] 文件     (1)功能该命令是显示指定文件以及下的所有文件占用系统数据块的情况&#xff0c;如果没有文件&#xff0c;默认为是当前工作目录     -a    显示所有文件对系统数据块的使用情况     -b    显示数据块大小时以字节…

c++详解【智能指针】

智能指针&#xff1f;是一个指针吗&#xff1f;这里给大家说的是&#xff0c;它不是一个指针&#xff0c;但它模拟了指针所具有的功能。那么&#xff0c;为什么要有智能指针的引入呢&#xff1f;看看下面的例子吧~ void FunTest() {int *p new int[10];FILE *pFile fopen(&qu…

python 使用 os的 popen(‘命令’) 如果命令行输出中 有中文乱码, 提示 'gbk' 无法解析的错误 解决办法

os.chdir(‘你的命令’) res os.popen(v.testcomman)print(tempstream.buffer.read().decode(encodingutf-8)&#xff09;

node.js async await 配合Promise对象使用

function getData(){return new Promise(function(resolve, reject){setTimeout(function(){var uname zhang;console.log(this is timeout);resolve(uname);}, 1000);}); } //await 配合 promiese 的 resolve 使用 就会真的等待 同步 async function test(){console.log(1);v…

c++【深度剖析shared_ptr】

shared_ptr解决了scoped_ptr管理单个对象的缺陷&#xff0c;且解决了防拷贝的问题。shared_ptr可以管理多个对象&#xff0c;并且实现了资源共享。 但是仍然存在一些问题&#xff0c;比如&#xff0c;我们熟悉的双向链表&#xff1a; struct Node { Node(const int& value…

centos重新安装yum

1.备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 2.下载新的CentOS-Base.repo 到/etc/yum.repos.d/ wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo 3. yum makecache GDB的安装 yum…

Electron 渲染进程,如何解决require is not defined的问题

mainWindow new BrowserWindow({webPreferences: {nodeIntegration: true}}) // nodeIntegration: true 加上这一句 就可以了 5.0以后默认是false

c++详解【new和delete】

说起new和delete&#xff0c;了解过c的人应该都知道吧&#xff0c;它是用来分配内存和释放内存的两个操作符。与c语言中的malloc和free类似。 c语言中使用malloc/calloc/realloc/free进行动态内存分配&#xff0c;malloc/calloc/realloc用来在堆上分配空间&#xff0c;free将申…

vim 的配置文件 #vim ~/.vimrc

set hlsearch set backspace2 set nu set showmode set ruler set autoindent syntax on set smartindent set tabstop4 set shiftwidth4 set expandtab imap { {}iV

关于tornado的异步耗时操作假设

tornado 如果遇到耗时的操作&#xff0c;可不可以这样 把耗时操作放在一个由 python进程池维护的 pool中&#xff0c; 用 webapi封装起来&#xff0c; 然后tornado 接收客户端请求后&#xff0c;遇到耗时操作就 与访问另一个webapi &#xff0c; webapi去调用进程池 这种模型不…

Stack/Queue与Vector/List的联系

Vector:(顺序表【数组存储】) 1.当申请的空间不足的时候&#xff0c;需要再次开辟一块更大的空间&#xff0c;并把值拷过去。 2.对于尾删和尾插是比较方便的&#xff0c;只需要改动最后一个元素即可。不会改动原有的空间。适用于多次重复的对尾部插删。 3.顺序存储&#xff…