zoukankan      html  css  js  c++  java
  • C++模版完全解析

    模版

    模版在C++中是一个很重要的概练,生活中的模版也是随处可见,比如制造工程师会
    哪一个模子去构造出一个一个的工件,C++程序员能够用模版去实例化y有相同操作
    不同类型的函数或者数据结构。简单的理解模版就是为省去重复,同时又比C中的宏容易调试
    因为C++编译器会做类型参数语法检查。

    C中的模版模拟

    想当年C++还没有出现模版的时候,C程序员可能为了写比较两个数的最大值写出如下几种类型

    int max_int(int x,int y){return  x>y?x:y}
    int max_double(double x,double y){return  x>y?x:y}
    int max_string(string x,string y){return x>y?x:y}//C++中
    

    这样的写法实在太繁杂,后来由发现用宏来写可能会舒服点儿,比如

    //macro.cpp 
    #include <iostream>
    using namespace std;
    #define MAX(T) 
    T max_##T(T x,T y)
    {
    return  x>y?x:y;
    }
    
    
    MAX(int)
    MAX(string)
    MAX(double)
    
    #define max(T)  max_##T
    
    
    int
    main(int argc, char ** argv){
    
            cout<<"int:"<<max(int) (2,3)<<endl;
            cout<<"double:"<<max(double) (3.3,1.2)<<endl;
            cout<<"string:"<<max(string) (string("welcome"),string("guiyang"))<<endl;
    
            cout<<endl;
    
            return  0 ;
    }
    

    这样看是比写三个方便了,在这个地方有几个东西需要说明

    • 1.我们最终任然是写了三个函数,只不过是通过 预处理器 的方式来为我们创建了
      因为宏只在预处理的时候有效
    • 2.一旦预处理完成我们的宏就不见了不能在符号表找到,不能加-g调试
    • 3.为了确定我们的函数生成在预处理阶段我们可以看一下预处理信息
    # 2 "macro.cpp" 2
    using namespace std;
    
    int max_int(int x,int y){return x>y?x:y;}
    string max_string(string x,string y){return x>y?x:y;}
    double max_double(double x,double y){return x>y?x:y;}
    
    int
    main(int argc, char ** argv){
    
     cout<<"int:"<<max_int (2,3)<<endl;
     cout<<"double:"<<max_double (3.3,1.2)<<endl;
     cout<<"string:"<<max_string (string("welcome"),string("guiyang"))<<endl;
    
     cout<<endl;
    
     return 0 ;
    }
    [lmg@localhost cpp]$ 
    

    C++中的模版

    C++中的模版也采用了相似的思想,不过将其变为了函数模版
    即定义出一个通用的函数,用类型参数形参表示具体的函数模版的类型

    • 语法:template <[typename|class] argsname1,[typename|class] argsname1,...> argsname funcname(argsname1 x,argsname2 y,...)
    • 调用:funcname<argsname1 ,argsname2,..>(x,y,...)
    • 注意:
      • 1.模版形参表和模版实参表要对应,可以有多个
      • 2.模版的声明和使用一般通常的编写手法是放在一个头文件中,需要用到的包含文件即可
      • 3.通常我们写C文件的时候会将声明放在头文件中,而将定义实现放在.c文件中,这是因为
        我们的C/C++通常的处理是声明可以重复但是定义是不能重复的。
      • 4.类型形参表是由调用的类型实参进行实例化,此过程完成在编译阶段,和上面的C实现的区别主要是
        C实现发生在预处理阶段,但是最终都会生成多份儿符号儿表

    代码实现

    #include <iostream>
    
    template <typename T>  //定义模版名称
    T max(T x, T y){
            return x>y?x:y;
    }
    
    
    int
    main(int argc, char ** argv){
    
            std::cout<<"int:"<<max<int>(3,2)<<std::endl;
            std::cout<<"doule:"<<max<double>(2.4,3.4)<<std::endl;
            std::cout<<"string:"<<max<std::string>("welcome","beijing")<<std::endl;
    }
    

    为了进一步分析模版实现的特点我们观察如下两个方面

    • 预处理模版的代码
    # 2 "template.cpp" 2
    
    template <typename T>
    T max(T x, T y){
     return x>y?x:y;
    }
    
    
    int
    main(int argc, char ** argv){
    
     std::cout<<"int:"<<max<int>(3,2)<<std::endl;
     std::cout<<"doule:"<<max<double>(2.4,3.4)<<std::endl;
     std::cout<<"string:"<<max<std::string>("welcome","beijing")<<std::endl;
    }
    

    从预处理结果可以看出预处理结果中模版代码并没有改变

    • 用nm命令查看连接结果符合表
    0000000000601248 d _DYNAMIC
    0000000000601410 d _GLOBAL_OFFSET_TABLE_
    0000000000400dd7 t _GLOBAL__I_main
    0000000000400fa8 R _IO_stdin_used
                     w _Jv_RegisterClasses
                     U _Unwind_Resume@@GCC_3.0
    0000000000400e3c W _Z3maxISsET_S0_S0_
    0000000000400e08 W _Z3maxIdET_S0_S0_
    0000000000400dec W _Z3maxIiET_S0_S0_
    0000000000400d97 t _Z41__static_initialization_and_destruction_0ii
                     U _ZNKSs7compareERKSs@@GLIBCXX_3.4
                     U _ZNSaIcEC1Ev@@GLIBCXX_3.4
                     U _ZNSaIcED1Ev@@GLIBCXX_3.4
                     U _ZNSolsEPFRSoS_E@@GLIBCXX_3.4
                     U _ZNSolsEd@@GLIBCXX_3.4
                     U _ZNSolsEi@@GLIBCXX_3.4
                     U _ZNSsC1EPKcRKSaIcE@@GLIBCXX_3.4
                     U _ZNSsC1ERKSs@@GLIBCXX_3.4
                     U _ZNSsD1Ev@@GLIBCXX_3.4
                     U _ZNSt8ios_base4InitC1Ev@@GLIBCXX_3.4
                     U _ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4
    00000000006014c0 B _ZSt4cout@@GLIBCXX_3.4
                     U _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@@GLIBCXX_3.4
    00000000006015e0 b _ZStL8__ioinit
    0000000000400e90 W _ZStgtIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_ES8_
                     U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@@GLIBCXX_3.4
                     U _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKSbIS4_S5_T1_E@@GLIBCXX_3.4
    0000000000400fe0 r _ZZL18__gthread_active_pvE20__gthread_active_ptr
    0000000000601228 d __CTOR_END__
    0000000000601218 d __CTOR_LIST__
    0000000000601238 D __DTOR_END__
    0000000000601230 d __DTOR_LIST__
    00000000004011d8 r __FRAME_END__
    0000000000601240 d __JCR_END__
    0000000000601240 d __JCR_LIST__
    00000000006014bc A __bss_start
                     U __cxa_atexit@@GLIBC_2.2.5
    00000000006014b8 D __data_start
    0000000000400f60 t __do_global_ctors_aux
    0000000000400b20 t __do_global_dtors_aux
    0000000000400fb0 R __dso_handle
                     w __gmon_start__
                     U __gxx_personality_v0@@CXXABI_1.3
    0000000000601213 d __init_array_end
    0000000000601213 d __init_array_start
    0000000000400ec0 T __libc_csu_fini
    0000000000400ed0 T __libc_csu_init
                     U __libc_start_main@@GLIBC_2.2.5
    00000000006014bc A _edata
    00000000006015e8 A _end
    0000000000400f98 T _fini
    0000000000400988 T _init
    0000000000400ad0 T _start
    0000000000400afc t call_gmon_start
    00000000006015d0 b completed.6349
    00000000006014b8 W data_start
    00000000006015d8 b dtor_idx.6351
    0000000000400b90 t frame_dummy
    0000000000400bb4 T main
                     w pthread_cancel
    

    从下面的符合表

    0000000000400e3c W _Z3maxISsET_S0_S0_
    0000000000400e08 W _Z3maxIdET_S0_S0_
    0000000000400dec W _Z3maxIiET_S0_S0_
    

    可以看出生成了三分儿模版函数实例

    必知知识点

    为了便于理解模版我觉得如下概练需要进一步强度

    • 1、宏替换发生在预处理阶段 -E参数可以看到处理结果
    • 2、模版函数实例化函数模版发生在编译阶段,可以nm命令通过分析二进制文件符合表发现
    • 3、函数模版和模版函数的形参表的顺序必须一致,因为简单理解其实现的是对应替换的功能,再加一点儿编译器做的类型检查
    • 4、模版形参必须是能支持函数模版的操作,比如一个用“>”符合的比较函数,其模版类型T必须能支持“>”符号或者重载了该运算符
    • 5、模版声明定义一般直接放入头文件中,和通常的声明放在头文件实现放在.C|.CPP文件略有不同
  • 相关阅读:
    nowcoderD Xieldy And His Password
    Codeforces681D Gifts by the List
    nowcoder80D applese的生日
    Codeforces961E Tufurama
    Codeforces957 Mahmoud and Ehab and yet another xor task
    nowcoder82E 无向图中的最短距离
    nowcoder82B 区间的连续段
    Codeforces903E Swapping Characters
    Codeforces614C Peter and Snow Blower
    Codeforces614D Skills
  • 原文地址:https://www.cnblogs.com/luomgf/p/5004562.html
Copyright © 2011-2022 走看看