【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】 

如果说模板类定义的是一种数据类型,那么模板函数定义的就是一种函数。既然是函数,那么就有输入数据和输出数据。和模板类的概念差不多,模板函数的初衷也是为了在函数操作上抽取共同的特性,屏蔽的是类型的不同和差异。我们可以通过下面一个简单的代码说明问题:

int int_compare(int a, int b)
{return a > b ? a : b;
}double double_compare(double a, double b)
{return a > b ? a : b;
}

上面的一段代码是取较大值的一段代码。两个函数之间最大的差别就是输入数据类型和输出数据类型之间的差别,那我们有没有一种办法可以屏蔽这种数据类型之间的差别呢?有。那就是函数模板:

template <typename type>
type compare(type a, type b)
{return a > b ? a : b;
}

可以看到,模板函数和普通函数没有什么区别,只是在函数的上面把类型抽象成了type,那么模板函数应该怎么使用呢?

246:      int i_value = compare(2, 3);
00401458   push        3
0040145A   push        2
0040145C   call        @ILT+10(compare) (0040100f)
00401461   add         esp,8
00401464   mov         dword ptr [ebp-4],eax
247:      double d_value = compare(2.3, 3.1);
00401467   push        4008CCCCh
0040146C   push        0CCCCCCCDh
00401471   push        40026666h
00401476   push        66666666h
0040147B   call        @ILT+5(compare) (0040100a)
00401480   add         esp,10h
00401483   fstp        qword ptr [ebp-0Ch]
248:  }

汇编代码表明,两个compare调用的函数地址并不是一致的。其中整数的compare地址是0x40100f,而double的地址是0x0040100a。这说明编译器在编译的时候帮我们同时生成了两个compare函数。所以说,模板类的本质就是在编译器增加判断处理工作的同时,减少手工的重复劳动。同时和模板类不一样,模板函数不需要显示定义函数的参数类型,这是因为可以从入参判断出函数的类型。

如果参数类型是 class类型呢? 我们可以试一试。首先定义基本class:

class data
{int value;
public:explicit data(int m): value(m) {}~data() {}int get_value() { return value;}int operator > (data& d) {return this->get_value() > d.get_value();}
};

接着,我们调用compare函数:

256:      data m(4), n(2);
0040148D   push        4
0040148F   lea         ecx,[ebp-10h]
00401492   call        @ILT+40(data::data) (0040102d)
00401497   mov         dword ptr [ebp-4],0
0040149E   push        2
004014A0   lea         ecx,[ebp-14h]
004014A3   call        @ILT+40(data::data) (0040102d)
004014A8   mov         byte ptr [ebp-4],1
257:      data p = compare(m,n);
004014AC   mov         eax,dword ptr [ebp-14h]
004014AF   push        eax
004014B0   mov         ecx,dword ptr [ebp-10h]
004014B3   push        ecx
004014B4   lea         edx,[ebp-18h]
004014B7   push        edx
004014B8   call        @ILT+15(compare) (00401014)
004014BD   add         esp,0Ch
258:  }

256行: data构造了两个基本变量m和n

257行: 我们调用模板函数compare, 函数地址为0x401014,注意dx为p的地址,也就是堆栈临时变量的地址

为了看到算术符>重载,我们跟进compare函数:

241:      return a > b ? a : b;
0040212B   lea         eax,[ebp+10h]
0040212E   push        eax
0040212F   lea         ecx,[ebp+0Ch]
00402132   call        @ILT+55(data::operator>) (0040103c)
00402137   test        eax,eax
00402139   je          compare+53h (00402143)
0040213B   lea         ecx,[ebp+0Ch]
0040213E   mov         dword ptr [ebp-18h],ecx
00402141   jmp         compare+59h (00402149)
00402143   lea         edx,[ebp+10h]
00402146   mov         dword ptr [ebp-18h],edx
00402149   mov         eax,dword ptr [ebp-18h]
0040214C   mov         dword ptr [ebp-10h],eax
0040214F   mov         ecx,dword ptr [ebp-10h]
00402152   mov         edx,dword ptr [ecx]
00402154   mov         eax,dword ptr [ebp+8]
00402157   mov         dword ptr [eax],edx
00402159   mov         ecx,dword ptr [ebp-14h]
0040215C   or          ecx,1
0040215F   mov         dword ptr [ebp-14h],ecx
00402162   mov         byte ptr [ebp-4],1
00402166   lea         ecx,[ebp+0Ch]
00402169   call        @ILT+25(data::~data) (0040101e)
0040216E   mov         byte ptr [ebp-4],0
00402172   lea         ecx,[ebp+10h]
00402175   call        @ILT+25(data::~data) (0040101e)
0040217A   mov         eax,dword ptr [ebp+8]

我们发现compare模板语句下面构建了很多汇编语句,有一些冗长,我们可以大略介绍一下:

(1)  开头调用call 0x0040103C函数就是调用重载运算符函数,[ebp-18h]表示即将被复制的是a数据还是b数据

(2) 比较返回结果后,开始复制数据,具体见0x402157,其中临时变量[ebp-14h]和临时变量[ebp-4]的操作可以忽略

(3) 函数返回前,对临时变量a和b进行析构处理,见代码0x402169和代码0x402175。

注意:

(1)编写模板函数前先保证自己的函数是编写正确的

(2)模板函数的优先级低于非模板函数

(3)模板函数的类型可以是自定义类型,也可以是c、c++语言的基本类型

(4)模板函数的使用经常和类的算术运算符混合使用,注意技巧

(5)模板函数中涉及指针部分的内容,务必注意

【预告: 下一篇模板主要讲述特化模板、缺省模板】

用汇编的眼光看c++(之模板函数)相关推荐

  1. 用汇编的眼光看C++(之 总结篇)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 早在八月份的时候,就陆陆续续写了二十多篇用汇编语言看C++的博客内容.在此为了做一个概括,也为 ...

  2. 用汇编的眼光看C++(之退出流程)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 无论是在判断还是在循环的过程中,通常在遇到合适的条件的时候就会退出相应的模块.跳出模块运行的方 ...

  3. 用汇编的眼光看C++ (之x86汇编)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 说到用汇编的眼光看C++语言,那么怎么阅读汇编代码就成了我们需要解决的一个问题.其实,实话说, ...

  4. 用汇编的眼光看C++(之模板类)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 如果类是一种确定的数据类型,那么模板就是一种对类的抽象.假设有这么一种类,它需要进行数据的计算 ...

  5. 从汇编的眼光看C++(之泛型编程)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 泛型编程其实不难.本质上说,泛型编程就是让通用的算法应用到所有的数据类型.具体来说,int是我 ...

  6. 从汇编的眼光看C++(之递归函数与模板类)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 递归,相信有过基本C语言经验的朋友都明白,就是函数自己调用自己.所以,本质上说,它和普通的函数 ...

  7. 用汇编的眼光看C++(之缺省模板、特化模板)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 缺省函数是C++的一个基本特色.缺省函数定义比较简单,也就是说,对于函数的某一个输入参数或者几 ...

  8. 用汇编的眼光看C++(之判断流程)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 在我们平常的编程当中,用于判断的地方很多,但主要有下面三种方式:if-else:switch: ...

  9. 用汇编的眼光看C++(之拷贝、赋值函数)

    拷贝构造函数和复制函数是类里面比较重要的两个函数.两者有什么区别呢?其实也很简单,我们可以举个例子,加入有这样一个类的定义: [cpp] view plaincopy class apple { pu ...

最新文章

  1. 在leangoo里怎么创建看板,更改看板名称?
  2. SQLServer特殊字符/生僻字与varchar
  3. linux内核线程创建销毁机制
  4. 解决 FtpClient 类无法导入 .
  5. [分布式] ------ 全局唯一id生成之雪花算法(Twitter_Snowflake)
  6. 风险意识培训教程(续)
  7. 信号问题可根除,苹果新款 iPhone 将搭载高通基带?
  8. golang解决TCP粘包问题
  9. 雅虎历任CEO的错误
  10. 曾经一学长的ACM总结帖,膜拜之。
  11. 用pdftocairo将PDF图片转成svg矢量图
  12. 河北古村落版画展开幕 河北这些古村落你值得去看看
  13. 苹果手机账号验证失败连接不上服务器,Apple ID登录连接服务器验证失败怎么解决?...
  14. 【我的Android进阶之旅】Configuration 'compile' is obsolete and has been replaced with 'implementation' and
  15. 如何从课堂派上扒取直播视频
  16. 尤雨溪 6 月 4 日的 Vue 技术分享
  17. 通过微信开发测试号进行微信登录
  18. QQ签名资料特殊字符大全
  19. bat: %~d0 %cd% %~dp0
  20. nginx:Url重写

热门文章

  1. 《社交网站界面设计(原书第2版)》——3.2 注册
  2. linux中断申请之request_threaded_irq 【转】
  3. 分享非常有用的Java程序(关键代码)(七)---抓屏程序
  4. 将android中如何调整Toast位置?
  5. 一个IO的传奇一生 (9) -- Noop和Deadline调度器
  6. 可关闭与最小化的右下角浮动广告代码
  7. 使用perl连接oracle数据库
  8. 南海区行政审批管理系统接口规范v0.3(规划) 2.业务申报API 2.1.businessApply【业务申报】...
  9. (转)C# 控制蜂鸣器发声
  10. Linux内核设计第四周——扒开系统调用三层皮