C++模版完全解析
2015-11-29 12:06
519 查看
模版
模版在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文件略有不同
相关文章推荐
- C++中的static关键字
- C++标准库Vector & Iterator用法
- C++ 关于 调用函数时值传递和引用的理解
- 用c语言进行“面向对象编程”------学习doubango源码心得------阿冬专栏!!!
- C++ STL (1)set容器用法、操作详细介绍
- c++ iterator(迭代器)分类及其使用
- 实现包含min函数的栈(C++版)
- c++中string类的详解
- C++箭头操作符重载
- c++之ASSERT断言的使用
- 36.c/c++程序员面试宝典-泛型编程
- 链表反转(C++版)
- C++伸展树自顶向下实现
- C++ map的基本操作和使用
- C++ Vector的基本操作和使用
- C++ premer Plus 第6版第6章第5题 &&cin>>初步理解
- java中基本数据类型和C语言中基本数据类型转换
- 提高C/C++运行效率以及避免出现Bug的20种方法
- 《C++ primer》英文第五版阅读笔记(二十四)——try控制块和异常处理
- 将数组A中的内容和数组B中的内容进行交换(数组一样大)