模板函数定义的就是一种函数。既然是函数,那么就有输入数据和输出数据。和模板类的概念差不多,模板函数的初衷也是为了在函数操作上抽取共同的特性,屏蔽的是类型的不同和差异。
模板函数的反汇编示例:
#include <stdio.h>
#include <stdlib.h>
template <typename type>
type compare(type a, type b)
{
return a > b ? a : b;
};
int main(void) {
int v = compare(2, 3);
double v2 = compare(2.5, 3.5);
printf("v=%d,v2=%f", v, v2);
}
main():
080483e4: push %ebp
080483e5: mov %esp,%ebp
080483e7: and $0xfffffff0,%esp
080483ea: sub $0x20,%esp
11 int v = compare(2, 3);
080483ed: movl $0x3,0x4(%esp)
080483f5: movl $0x2,(%esp)
080483fc: call 0x8048444 <compare<int>(int, int)>
08048401: mov %eax,0x1c(%esp)
12 double v2 = compare(2.5, 3.5);
08048405: fldl 0x8048580
0804840b: fstpl 0x8(%esp)
0804840f: fldl 0x8048588
08048415: fstpl (%esp)
08048418: call 0x8048459 <compare<double>(double, double)>
0804841d: fstpl 0x10(%esp)
13 printf("v=%d,v2=%f", v, v2);
08048421: fldl 0x10(%esp)
08048425: fstpl 0x8(%esp)
08048429: mov 0x1c(%esp),%eax
0804842d: mov %eax,0x4(%esp)
08048431: movl $0x8048570,(%esp)
08048438: call 0x8048300 <printf@plt>
0804843d: mov $0x0,%eax
14 }
08048442: leave
08048443: ret
5 type compare(type a, type b)
compare<int>(int, int):
08048444: push %ebp
08048445: mov %esp,%ebp
7 return a > b ? a : b;
08048447: mov 0x8(%ebp),%eax
0804844a: cmp 0xc(%ebp),%eax
0804844d: jle 0x8048454 <compare<int>(int, int)+16>
0804844f: mov 0x8(%ebp),%eax
08048452: jmp 0x8048457 <compare<int>(int, int)+19>
08048454: mov 0xc(%ebp),%eax
8 };
08048457: pop %ebp
08048458: ret
5 type compare(type a, type b)
compare<double>(double, double):
08048459: push %ebp
0804845a: mov %esp,%ebp
0804845c: sub $0x10,%esp
0804845f: mov 0x8(%ebp),%eax
08048462: mov %eax,-0x8(%ebp)
08048465: mov 0xc(%ebp),%eax
08048468: mov %eax,-0x4(%ebp)
0804846b: mov 0x10(%ebp),%eax
0804846e: mov %eax,-0x10(%ebp)
08048471: mov 0x14(%ebp),%eax
08048474: mov %eax,-0xc(%ebp)
7 return a > b ? a : b;
08048477: fldl -0x8(%ebp)
0804847a: fldl -0x10(%ebp)
0804847d: fxch %st(1)
0804847f: fucomip %st(1),%st
08048481: fstp %st(0)
08048483: seta %al
08048486: test %al,%al
08048488: je 0x804848f <compare<double>(double, double)+54>
0804848a: fldl -0x8(%ebp)
0804848d: jmp 0x8048492 <compare<double>(double, double)+57>
0804848f: fldl -0x10(%ebp)
8 };
08048492: leave
08048493: ret
汇编代码表明,两个compare调用的函数地址并不是一致的。其中整数的compare地址是0x8048444,而double的地址是 0x8048459。这说明编译器在编译的时候帮我们同时生成了两个compare函数。所以说,模板类的本质就是在编译器增加判断处理工作的同时,减 少手工的重复劳动。模板函数不需要显示定义函数的参数类型,这是因为可以从入参判断出函数的类型。
#include <stdio.h>
#include <stdlib.h>
template<typename type>
class data_process {
type a;
type b;
public:
data_process(type m, type n) :
a(m), b(n) {
}
~data_process() {
}
type add() {
return a + b;
}
type sub() {
return a - b;
}
};
int main(void) {
data_process<int> data1(1,2);
data_process<double> data2(1.2, 2.3);
printf("v1=%d,v2=%f\n", data1.add(), data2.add());
}
main():
080484f4: push %ebp
080484f5: mov %esp,%ebp
080484f7: push %ebx
080484f8: and $0xfffffff0,%esp
080484fb: sub $0x50,%esp
24 data_process<int> data1(1,2);
080484fe: movl $0x2,0x8(%esp)
08048506: movl $0x1,0x4(%esp)
0804850e: lea 0x48(%esp),%eax
08048512: mov %eax,(%esp)
08048515: call 0x80485b4 <data_process<int>::data_process(int, int)>
25 data_process<double> data2(1.2, 2.3);
0804851a: fldl 0x8048710
08048520: fstpl 0xc(%esp)
08048524: fldl 0x8048718
0804852a: fstpl 0x4(%esp)
0804852e: lea 0x38(%esp),%eax
08048532: mov %eax,(%esp)
08048535: call 0x80485d0 <data_process<double>::data_process(double, double)>
26 printf("v1=%d,v2=%f\n", data1.add(), data2.add());
0804853a: lea 0x38(%esp),%eax
0804853e: mov %eax,(%esp)
08048541: call 0x804861a <data_process<double>::add()>
08048546: fstpl 0x28(%esp)
0804854a: lea 0x48(%esp),%eax
0804854e: mov %eax,(%esp)
08048551: call 0x8048608 <data_process<int>::add()>
08048556: fldl 0x28(%esp)
0804855a: fstpl 0x8(%esp)
0804855e: mov %eax,0x4(%esp)
08048562: movl $0x8048700,(%esp)
08048569: call 0x8048410 <printf@plt>
0804856e: lea 0x38(%esp),%eax
08048572: mov %eax,(%esp)
08048575: call 0x8048602 <data_process<double>::~data_process()>
0804857a: lea 0x48(%esp),%eax
0804857e: mov %eax,(%esp)
08048581: call 0x80485ca <data_process<int>::~data_process()>
08048586: mov $0x0,%eax
27 }
0804858b: mov -0x4(%ebp),%ebx
0804858e: leave
0804858f: ret
08048590: mov %eax,%ebx
26 printf("v1=%d,v2=%f\n", data1.add(), data2.add());
08048592: lea 0x38(%esp),%eax
08048596: mov %eax,(%esp)
08048599: call 0x8048602 <data_process<double>::~data_process()>
0804859e: lea 0x48(%esp),%eax
080485a2: mov %eax,(%esp)
080485a5: call 0x80485ca <data_process<int>::~data_process()>
080485aa: mov %ebx,%eax
080485ac: mov %eax,(%esp)
080485af: call 0x8048430 <_Unwind_Resume@plt>
9 data_process(type m, type n) :
data_process<int>::data_process(int, int):
080485b4: push %ebp
080485b5: mov %esp,%ebp
10 a(m), b(n) {
080485b7: mov 0x8(%ebp),%eax
080485ba: mov 0xc(%ebp),%edx
080485bd: mov %edx,(%eax)
080485bf: mov 0x8(%ebp),%eax
080485c2: mov 0x10(%ebp),%edx
080485c5: mov %edx,0x4(%eax)
11 }
080485c8: pop %ebp
080485c9: ret
13 ~data_process() {
data_process<int>::~data_process():
080485ca: push %ebp
080485cb: mov %esp,%ebp
14 }
080485cd: pop %ebp
080485ce: ret
080485cf: nop
9 data_process(type m, type n) :
data_process<double>::data_process(double, double):
080485d0: push %ebp
080485d1: mov %esp,%ebp
080485d3: sub $0x10,%esp
080485d6: mov 0xc(%ebp),%eax
080485d9: mov %eax,-0x8(%ebp)
080485dc: mov 0x10(%ebp),%eax
080485df: mov %eax,-0x4(%ebp)
080485e2: mov 0x14(%ebp),%eax
080485e5: mov %eax,-0x10(%ebp)
080485e8: mov 0x18(%ebp),%eax
080485eb: mov %eax,-0xc(%ebp)
10 a(m), b(n) {
080485ee: mov 0x8(%ebp),%eax
080485f1: fldl -0x8(%ebp)
080485f4: fstpl (%eax)
080485f6: mov 0x8(%ebp),%eax
080485f9: fldl -0x10(%ebp)
080485fc: fstpl 0x8(%eax)
11 }
080485ff: leave
08048600: ret
08048601: nop
13 ~data_process() {
data_process<double>::~data_process():
08048602: push %ebp
08048603: mov %esp,%ebp
14 }
08048605: pop %ebp
08048606: ret
08048607: nop
15 type add() {
data_process<int>::add():
08048608: push %ebp
08048609: mov %esp,%ebp
16 return a + b;
0804860b: mov 0x8(%ebp),%eax
0804860e: mov (%eax),%edx
08048610: mov 0x8(%ebp),%eax
08048613: mov 0x4(%eax),%eax
08048616: add %edx,%eax
17 }
08048618: pop %ebp
08048619: ret
15 type add() {
data_process<double>::add():
0804861a: push %ebp
0804861b: mov %esp,%ebp
16 return a + b;
0804861d: mov 0x8(%ebp),%eax
08048620: fldl (%eax)
08048622: mov 0x8(%ebp),%eax
08048625: fldl 0x8(%eax)
08048628: faddp %st,%st(1)
17 }
0804862a: pop %ebp
0804862b: ret
针对每一个类型,模板的构造函数、析构函数、成员函数都要独立生成,这从上面的函数地址就可以看出来。所以模板的本质就是对 不同数据类型的相似性操作进行共同属性提取,合成模板。在应用的时候,编译器根据我们使用中的数据类型独立生成每一个类,构建每一个基本运算变量和运算函 数,仅此而已。
分享到:
相关推荐
### C++模板元编程技术与应用 #### 一、历史背景 C++模板元编程(Template Metaprogramming,简称TMP)的历史可以追溯至1994年。在这年的一次C++标准委员会会议上,Erwin Unruh展示了一段特殊的代码,能够利用编译...
《深入C++对象模型》、《STL源码剖析》以及《C和指针》、《C陷阱和缺陷》等书则从更深入的角度剖析了C++的对象模型、标准模板库的实现以及C和C++语言的高级特性及其潜在风险。阅读这些书籍可以加深对C++和C语言的...
4. **面向对象编程**:C++是面向对象的语言,面试题可能包括类、对象、封装、继承、多态等概念,以及虚函数、模板、STL库的使用。 5. **异常处理**:C++中的try-catch语句和异常处理机制,以及何时应该使用异常处理...
由于没有具体的标签信息,我们可以从C++的基础知识、核心概念、编程实践等多个角度来探讨这个主题。 C++是一种强大的、面向对象的编程语言,由Bjarne Stroustrup于1979年在贝尔实验室开发,作为C语言的扩展。它在...
- **反向审视**:Eckel经常通过具体的例子引导读者从C++编译器的角度反向思考C++语言的实现原理,这种视角能够帮助读者深入理解语言特性。 - **澄清混淆点**:书中特别注重澄清学习过程中容易产生混淆的概念,通过...
4. **从汇编角度看C++编译器优化**:解析编译器如何进行自动优化,帮助理解代码的底层执行机制。 5. **C++11起的多线程编程**:介绍从mutex到无锁并行编程的方法,为并行编程打下坚实的基础。 6. **并行编程常用框架...
- 在进行向量运算时,要注意不同运算的意义,比如利用点积判断角度关系,叉积判断旋转方向。 - 距离计算时,注意区分不同对象间的距离公式。 ##### 1.3 模板代码 - **点积计算**: ```cpp double dot(Vector a, ...
- `"fsinx %1, %0"`:表示汇编指令模板,其中`%1`对应第一个参数(输入角度),`%0`对应第二个参数(结果)。 - `": "=f" (result)"`:指定输出变量约束。`"=f"`表示该变量会被存储在一个浮点寄存器中,并且会被...
(3)汇编角度: - 初始化引用`int &a = i;`,`lea eax,[i]`获取i的地址,然后`mov dword ptr [a],eax`看似将地址存入a的内存,实际上这只是将地址赋值给引用a,a并没有独立的存储空间。 总结来说,数组、指针和...
2. **计算机技能**:除了专业课程,他还学习了计算机相关的课程,如计算机组织与结构、硬件及接口电路设计、汇编语言、C/C++程序设计、软件工程以及计算机网络原理,这表明他在IT领域有扎实的基础,能够进行软件开发...
1. **创建MDI工程**:在Visual C++ 6.0的"新建"对话框中,选择"工程" -> "MFC应用程序",然后在向导中选择"MDI应用程序"模板。 2. **设计视图类**:为每个视图创建一个继承自CView的类,定义并实现视图特定的绘图和...
** 从纯粹的数据封装角度,友元确实打破了封装规则,因为它暴露了类的内部细节。但在实际应用中,适当使用友元可以简化代码设计,只要控制好访问权限,避免滥用,友元仍然是一种有用的机制。 #### 提高篇:进阶C++...
### 数据结构(C#语言版)知识点解析 #### 一、数据结构教材的选择与现状 - **背景**: 自从美国唐·欧·克努特教授用汇编语言编写了...无论是从理论学习还是实际操作的角度来看,本书都能够提供全面的支持和指导。
从C++的角度,我们可以探讨以下知识点: 1. **基本语法**:包括变量声明、数据类型(如int, char, float, double等)、操作符(如+,-,*,/,==,!=等)、控制结构(if-else, switch-case, for, while循环)。 2....
综上所述,为了更好地准备2024年的嵌入式大厂面试,需要从多个角度进行准备,不仅要加强理论知识的学习,还要注重实践能力的培养。同时,持续关注技术动态,不断提升自身的技术水平和个人能力。
那样,从调用程序的角度来看,它们所得到的全部是空闲的、开放的内存。然后,当通过 free() 将该指针传递回来时,我们只需要倒退几个内存字节就可以再次找到这个结构。 在讨论分配内存之前,我们将先讨论释放,...
18.2 在 C #代码中调用 C++和 VB 编写的组件 .240 18.3 版 本 控 制 .249 18.4 代 码 优 化 .252 18.5 小 结 .254 第五部分 附 录 .255 附录 A 关 键 字.255 附录 B 错 误 码.256 附录 C .Net 名字空间...