作者: Andy. 时间: 2016-07-20 00:05:11
C++中的泛型也是非常简单的,和C#中差不多。唯一的难度是在语法上面。来看看C++中泛型的语法:
#include <iostream> using namespace std; template<typename T> T Calcu(T a, T b){ return a + b; } int main(){ int a1 = 1; int b1 = 2; char a2 = '+'; char b2 = '-'; cout << Calcu<int>(a1, b1) << endl; cout << Calcu (a2, b2) << endl; return 0; }
看看运行结果:
PS G:\Blog\code\T> vim .\basic.cpp PS G:\Blog\code\T> g++ .\basic.cpp PS G:\Blog\code\T> .\a.exe 3 X
似乎并没有什么问题。我们知道编译后都会翻译成汇编代码,我们可以通过” PS G:\Blog\code\T> g++ .\basic.cpp -S”查看一下其汇编代码,看看具体的内部实现。上面的代码包含了cout、endl和操作符的重写,如果直接生成汇编代码,太多call,不便于分析。所以我们可以改改:
#include <iostream> using namespace std; template<typename T> T Calcu(T a, T b){ return a + b; } int main(){ int a1 = 1; int b1 = 2; char a2 = '+'; char b2 = '-'; cout << Calcu<int>(a1, b1) << endl; cout << Calcu (a2, b2) << endl; return 0; }
生成的汇编代码:
.file "basic.cpp" .lcomm __ZStL8__ioinit,1,1 .def ___main; .scl 2; .type 32; .endef .text .globl _main .def _main; .scl 2; .type 32; .endef _main: LFB1003: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $-16, %esp subl $32, %esp call ___main movl $1, 28(%esp) movl $2, 24(%esp) movb $43, 23(%esp) movb $45, 22(%esp) movl 24(%esp), %eax movl %eax, 4(%esp) movl 28(%esp), %eax movl %eax, (%esp) call __Z5CalcuIiET_S0_S0_ movsbl 22(%esp), %edx movsbl 23(%esp), %eax movl %edx, 4(%esp) movl %eax, (%esp) call __Z5CalcuIcET_S0_S0_ movl $0, %eax leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc LFE1003: .section .text$_Z5CalcuIiET_S0_S0_,"x" .linkonce discard .globl __Z5CalcuIiET_S0_S0_ .def __Z5CalcuIiET_S0_S0_; .scl 2; .type 32; .endef __Z5CalcuIiET_S0_S0_: LFB1004: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 movl 8(%ebp), %edx movl 12(%ebp), %eax addl %edx, %eax popl %ebp .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc LFE1004: .section .text$_Z5CalcuIcET_S0_S0_,"x" .linkonce discard .globl __Z5CalcuIcET_S0_S0_ .def __Z5CalcuIcET_S0_S0_; .scl 2; .type 32; .endef __Z5CalcuIcET_S0_S0_: LFB1005: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 subl $8, %esp movl 8(%ebp), %edx movl 12(%ebp), %eax movb %dl, -4(%ebp) movb %al, -8(%ebp) movzbl -4(%ebp), %edx movzbl -8(%ebp), %eax addl %edx, %eax leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc LFE1005: .text .def ___tcf_0; .scl 3; .type 32; .endef ___tcf_0: LFB1007: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 subl $8, %esp movl $__ZStL8__ioinit, %ecx call __ZNSt8ios_base4InitD1Ev leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc LFE1007: .def __Z41__static_initialization_and_destruction_0ii; .scl 3; .type 32; .endef __Z41__static_initialization_and_destruction_0ii: LFB1006: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 subl $24, %esp cmpl $1, 8(%ebp) jne L8 cmpl $65535, 12(%ebp) jne L8 movl $__ZStL8__ioinit, %ecx call __ZNSt8ios_base4InitC1Ev movl $___tcf_0, (%esp) call _atexit L8: leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc LFE1006: .def __GLOBAL__sub_I_main; .scl 3; .type 32; .endef __GLOBAL__sub_I_main: LFB1008: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5, -8 movl %esp, %ebp .cfi_def_cfa_register 5 subl $24, %esp movl $65535, 4(%esp) movl $1, (%esp) call __Z41__static_initialization_and_destruction_0ii leave .cfi_restore 5 .cfi_def_cfa 4, 4 ret .cfi_endproc LFE1008: .section .ctors,"w" .align 4 .long __GLOBAL__sub_I_main .ident "GCC: (GNU) 4.9.3" .def __ZNSt8ios_base4InitD1Ev; .scl 2; .type 32; .endef .def __ZNSt8ios_base4InitC1Ev; .scl 2; .type 32; .endef .def _atexit; .scl 2; .type 32; .endef
好多,其实只需要看26、31行就够了。咦,居然调用的函数不一样,但是我们在C++写的代码是一样的,,,,可以看出,在C++的语法上,我们调用的一个函数,但是实际上在编译的时候编译器是将其翻译成了两个不同的函数,原本还以为编译器这么聪明,知道我们想的啥呢~看来是想多了。
接下来说说重载匹配,在定义的多个函数与函数模板相同的时候,优先考虑普通函数。如果有多个函数模板与其匹配,选择最好的一个。说起来太复杂了~大家写个例子试试就好了,很简单,没什么说的。
类的泛型也是一样的,都是语法上的难度,如果语法是在记不住,参考一下C#里的泛型,语言都没啥大区别。来个类的泛型~很简单:
#include <iostream> using namespace std; template<typename T> class Parent { public: Parent(T a) { this->a = a; } void SetA(T a) { this->a = a; } T GetA() { return this->a; } protected: private: T a; }; class Child : public Parent<int> { public: Child(int a, int b) : Parent<int>(a) { this->b = b; } private: int b; }; int main() { Parent<int> a(10); Child b1(1, 2); }
全文结束,大家有兴趣的话可以试试友元函数的泛型(声明和函数体分离),还有静态的。非常有意思~