不太清楚模板,也没怎么用过,无奈最近看代码,老是出现模板,觉得一来看代码费劲了,而来,这个东西同事说很好,我也看看,如果好的话,我写的时候也用用,看了俩例子,记录下来,时而温习
温故而知新,而且每次温习必定有不同的体会。
实现一个加法的功能
首先看下,不用模板的代码
#include <iostream>
using namespace std;
int add(int a, int b);
float add(float a, float b);
float addFloat(float a, float b);
int main (int argc, const char * argv[])
{
int x = 1, y = 2;
cout<<"x +y = "<<add(x, y)<<endl;
float m = 1.0f, n = 2.0f;
cout<<"m + n = "<<add(m, n)<<endl;
return 0;
}
int add(int a, int b){
return a + b;
}
float add(float a, float b){
return a + b;
}
float addFloat(float a, float b){
return a + b;
}
运行可以得到结果
x +y = 3
m + n = 3
Program ended with exit code: 0
然后,这个应用了c++的重载的功能,两个函数,但是都叫一个名字,不然,真得用下面那个addFloat函数进行计算,但是重载跟模板应该说是有关系的,都是为了把类型忽略掉,但是又不同,具体后面阐述
现在用模板,模板是为了忽略掉类型,所以,再也没有int,float了,统一用一个类型T代替,然后调用的时候,再把类型传过来,这样就知道T的具体类型了,代码看下
#include <iostream>
using namespace std;
template<typename T>
T add(T a, T b);
int main (int argc, const char * argv[])
{
int x = 1, y = 2;
cout<<"x +y = "<<add(x, y)<<endl;
float m = 1.0f, n = 2.0f;
cout<<"m + n = "<<add(m, n)<<endl;
return 0;
}
template<typename T>
T add(T a, T b){
return a + b;
}
同样输出了正确的结果,这个时候,即使又来了新的类型,也能输出正确的结果,但是如果只用重载的话,就会失败了
模板和重载
重载是函数名相同,参数类型或者参数个数不同的函数,虽然在写的时候他们的名字是相同的,但是其实编译的时候并不相同,
看一下第一次的重载实现的可执行文件的汇编代码
================ B E G I N O F P R O C E D U R E ================
; Basic Block Input Regs: rsp rsi rdi - Killed Regs: rax rbp rsi
__Z3addii_100000d50: // add(int, int)
0000000100000d50 55 push rbp ; XREF=0x100000cd1
0000000100000d51 4889E5 mov rbp, rsp
0000000100000d54 897DFC mov dword [ss:rbp-0x0+var_m4], edi
0000000100000d57 8975F8 mov dword [ss:rbp-0x0+var_m8], esi
0000000100000d5a 8B75FC mov esi, dword [ss:rbp-0x0+var_m4]
0000000100000d5d 0375F8 add esi, dword [ss:rbp-0x0+var_m8]
0000000100000d60 89F0 mov eax, esi
0000000100000d62 5D pop rbp
0000000100000d63 C3 ret
; endp
0000000100000d64 6666662E0F1F840000000000 nop word [cs:rax+rax+0x0]
================ B E G I N O F P R O C E D U R E ================
; Basic Block Input Regs: rsp xmm0 xmm1 - Killed Regs: rbp xmm0
__Z3addff_100000d70: // add(float, float)
0000000100000d70 55 push rbp ; XREF=0x100000d20
0000000100000d71 4889E5 mov rbp, rsp
0000000100000d74 F30F1145FC movss dword [ss:rbp-0x0+var_m4], xmm0
0000000100000d79 F30F114DF8 movss dword [ss:rbp-0x0+var_m8], xmm1
0000000100000d7e F30F1045FC movss xmm0, dword [ss:rbp-0x0+var_m4]
0000000100000d83 F30F5845F8 addss xmm0, dword [ss:rbp-0x0+var_m8]
0000000100000d88 5D pop rbp
0000000100000d89 C3 ret
; endp
0000000100000d8a 660F1F440000 nop word [ds:rax+rax+0x0]
可以看到两个函数被编译成了两个函数,名字不一样,然后main函数调用的时候,也会调用call 指令指向不同的地址
那么其实跟写成addFloat没什么区别,只是在写法上和可读性上提升了很多
模板呢?会怎么样
看下汇编代码
================ B E G I N O F P R O C E D U R E ================
; Basic Block Input Regs: rsp rsi rdi - Killed Regs: rax rbp rsi
__Z3addIiET_S0_S0__100000df0: // int add<int>(int, int)
0000000100000df0 55 push rbp ; XREF=0x100001058
0000000100000df1 4889E5 mov rbp, rsp
0000000100000df4 897DFC mov dword [ss:rbp-0x0+var_m4], edi
0000000100000df7 8975F8 mov dword [ss:rbp-0x0+var_m8], esi
0000000100000dfa 8B75FC mov esi, dword [ss:rbp-0x0+var_m4]
0000000100000dfd 0375F8 add esi, dword [ss:rbp-0x0+var_m8]
0000000100000e00 89F0 mov eax, esi
0000000100000e02 5D pop rbp
0000000100000e03 C3 ret
; endp
0000000100000e04 6666662E0F1F840000000000 nop word [cs:rax+rax+0x0]
================ B E G I N O F P R O C E D U R E ================
; Basic Block Input Regs: rsp xmm0 xmm1 - Killed Regs: rbp xmm0
__Z3addIfET_S0_S0__100000e10: // float add<float>(float, float)
0000000100000e10 55 push rbp ; XREF=0x100001050
0000000100000e11 4889E5 mov rbp, rsp
0000000100000e14 F30F1145FC movss dword [ss:rbp-0x0+var_m4], xmm0
0000000100000e19 F30F114DF8 movss dword [ss:rbp-0x0+var_m8], xmm1
0000000100000e1e F30F1045FC movss xmm0, dword [ss:rbp-0x0+var_m4]
0000000100000e23 F30F5845F8 addss xmm0, dword [ss:rbp-0x0+var_m8]
0000000100000e28 5D pop rbp
0000000100000e29 C3 ret
; endp
同样有两个,那我如果再调用一次呢,增加一个string的
string u = "aaa", v = "bbb";
cout<<"u + v = " <<add(u, v)<<endl;
相应的汇编代码,也会增加一个函数
================ B E G I N O F P R O C E D U R E ================
; Basic Block Input Regs: rdi - Killed Regs: rax rsp rbp
__Z3addISsET_S0_S0__100001b10: // std::string add<std::string>(std::string, std::string)
0000000100001b10 55 push rbp ; XREF=0x100002060
0000000100001b11 4889E5 mov rbp, rsp
0000000100001b14 4883EC10 sub rsp, 0x10
0000000100001b18 4889F8 mov rax, rdi
0000000100001b1b 488945F8 mov qword [ss:rbp-0x10+var_8], rax
0000000100001b1f E80A010000 call imp___stubs___ZStplIcSt11char_traitsIcESaIcEESbIT_T0_T1_ERKS6_S8_
0000000100001b24 4883C410 add rsp, 0x10
0000000100001b28 5D pop rbp
0000000100001b29 C3 ret
; endp
0000000100001b2a 660F1F440000 nop word [ds:rax+rax+0x0]
看来模板是用到的时候展开,但是函数不用显示的传递变量的类型,他自己就会识别了