十五、C++中的模板templates
本部分讨论C++中的模板templates
在别的语言中,比如java、c、c#等托管语言中,模板类似泛型的概念,但模板比泛型要强大得多。模板有点像宏,而泛型却非常受限于类型系统以及其他很多因素。同时模板也是一个巨大的、复杂的话题,本部分仅仅是浅浅的入门。
1、什么是模板?
模板就是基于你给定的一套规则让编译器为你写代码。或者通俗的说就是,你写个模板,里面抠出一些空,这些空填上不同的东西,就是一个可用的对象。或者我举个例子,比如开发票,其实发票的格式都是一样的,只有抬头、金额、数量等几个要素不一样。你把空发票就可以看出模板,里面的抬头、金额、数量等几个地方是空的,你只要根据不同的客户填上不同的信息,每个客户的发票就开好了。通俗的理解,模板就是那个空发票。你把要填的信息填到对应的空里面,就你生成一张特定客户的发票了。
所以模板就是把代码的某些部分挖掉,然后传给编译器挖掉部分要填的内容,编译器就帮你完成这段代码了。所以说模板就是你给编译器一个套路,然后再给要填的空的答案,编译器就自动帮你完成了。
2、为什么要用模板?
如上左图所示,我只是想写一个func函数,但是我得要允许func函数可以接受各种类型的参数,比如整型、字符串、浮点型等等,那此时我就得写好几个函数重载,如上图A处。而且这些重载函数除了参数类型不一样外,其他地方都一模一样。手动重复写A处这么多差别不大的函数太费劲。如果我写一个模板,其他都写全,就把参数类型空出来,然后我给编译器传入那个空的答案,编译器像填空一样帮我填上就行了。这样我就省力很多了。这就是template诞生的初衷。
说明:cout是可以接受任何基本类型的或者说C++内置类型,就是我们现在正在使用的这些类型。
3、template的语法
上右图B处是template的语法:template单词就表示你定义了一个模板;尖括号里面的typename是模板的参数;T是名字,你可以随便取。
T用在C处。也就是T可以在整个模板代码中使用,来代替任何出现类型的地方。比如如果代码中出现int value,我们就可以写成T value;再比如如果出现string value,我们也可以写成T value。
当你定义了一个模板后(B处),编译器就会在编译期评估这个模板。所以上右图B、C处的代码不是真正的代码,func函数也不是一个真正的函数,它只是模板的一部分。只有当我们实际调用它的时候(D处),func函数才被真正的创建,创建时也是根据我们传入的参数类型,T才被替换,func才被创建出来,并作为源代码的一部分进行编译的。所以,比如MSVC编译器就不会对你不使用的模板错误进行报错,但是比如Clang等一些编译器会报错。
4、模板的工作原理
当我们调用func("hello")时,模板的另一个版本的函数就会被编译器创造(把尖括号中的类型替换T),并复制到模板下面,然后才编译。
所以模板的工作原理就是,当你调用模板中的函数时,编译器就根据你给它的信息,把该填的空都对应填上,并将生成的代码复制到模板后面即可。
模板不仅可以让编译器帮我们写函数,还可以写类。事实上,大量的C++标准模板库同样完全使用了模板,下面我们再展示如何写类的模板:
上面右图我们创造数组的方式,和C++标准模板库创造array数组的方式一样。记不记得array数组也是让我们在尖括号里面提供两个参数,一个是数组的类型,一个是数组的长度。所以模板有点像C++的meta programming(元编程),就是编译器在编译时实际还进行了编程。
了解模板模板的工作原理后,我们自然可以判断,不是什么情况都适合使用模板的,有的个性化非常强的工作,你就还是别使用模板了。如果是一些重复性很强的工作,比如日志系统,比如要写很多类型的重载函数,这样的工作使用模板就是非常合适的。
十六、C++中的宏
待续。。。。。