??????? 模板函数定义的就是一种函数。既然是函数,那么就有输入数据和输出数据。和模板类的概念差不多,模板函数的初衷也是为了在函数操作上抽取共同的特性,屏蔽的是类型的不同和差异。
??????? 模板函数的反汇编示例:
????
class="cpp">#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????? 针对每一个类型,模板的构造函数、析构函数、成员函数都要独立生成,这从上面的函数地址就可以看出来。所以模板的本质就是对 不同数据类型的相似性操作进行共同属性提取,合成模板。在应用的时候,编译器根据我们使用中的数据类型独立生成每一个类,构建每一个基本运算变量和运算函 数,仅此而已。