博客文章

C++中的泛型和函数模板

作者: 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);
}

全文结束,大家有兴趣的话可以试试友元函数的泛型(声明和函数体分离),还有静态的。非常有意思~