++的语法真是太多了
首先,栈可以使用指针压栈吗?
当然可以,这里写一个简单的栈
提到指针,可能会想到字符串,我们可以有以下几种传入
string str; 最简单的传入,得益于C++提供的强大的string类,我们可以轻松的完成这个操作
char str[size]; C风格的字符串,但对某些模板类方法可能不可用
char * str = new char[size];
对于我们实现的这个类来说,至少对输入数据不做修改是不可行的
我们可以看见,这样压栈元素的是一个指针,在没有人为的干预下,这个指针是指向的内存是不会改变的,我们重复对str输入是无意义的;
妥当的做法还是让程序提供一个指针数组(一般是二维数组),其中每个指针都指向不同的字符串。栈的任务是管理指针,而不是创建指针。
运行结果
可以看见重复输出了eee
模板的递归使用
我们知道有array这个模板类,所谓的递归也就是模板套模板罢了
外层的array中包含 五个 包含 三个int 的array 模板
描述成语言可能不好理解,不过看一下源码就懂了
可像二维数组那样进行操作
接受多个类型的模板
假设我们要用一个二元组存放数据,这两个元类型可能是相同的,也可能是不同的,那么我们如何编写模板类
简单模拟一下二元组pair,非常简单,可能动动手就写出来了
pair.h
main.cpp
默认类型模板
就像默认函数参数那样,如果没声明可以自动就变为默认值,模板类如果没有声明类型,就会自动变为默认类型
模板类的具体化
1.隐式实例化
就像模板函数一样,模板类在没有指出实例化对象时编译器会帮助我们自动实例化我们传入的类型。在编译器需要对象之前,不会生成隐式的实例化。
这只是声明一个模板类指针,并没有实例化。
2.显示实例化
使用关键字template并指出所需的类型来声明类时,编译器将会生成类声明的显式实例化。声明必须位于模板定于的名称空间中。
下面显式声明一个<string,int>的类
在这种情况下,虽然没有创建或者提及对象,编译器也将生成类声明(包括方法定义)。和隐式实例化一样,也将根据通用模板来生成具体化。
3.显式具体化
显式具体化是特定类型的定义。有些类型我们需要有特定的模板类方法处理它。这时候就要用到显式具体化。显式具体化是模板类通用性的进一步补充。
定义格式如下
4.部分具体化
即限制模板的通用性,假如 我们写了只可以处理整数的模板类,不想让浮点类型使用这个模板,我们可以这样做
这样模板类的第二个类型只能是整形
如果有多个模板可供选择,编译器将会选择具体化程度最高的
特别注意为指针提供的模板具体化
若没有第二个模板声明,编译器会将T变为char *
而第二个不是将T变为char **而是变为char
成员模板
模板可用作结构体,类或者模板的成员。要完全实现STL的设计,必须使用这项特性。下面写一个简单的模板类的嵌套
将模板类作为参数
模板类可以包含类型参数(type)也可以包含非类型参数(int a),同样的,模板类也可以包含本身就是模板的参数,是C++11的特性,用于实现STL
其实,模板类的递归就是将模板类作为了参数
语法形式很好理解
注意中间没有逗号
其中 /**/template <typename type_name> class/**/是类型,后面的type_name是参数
假设我们要对二元组进行栈处理,那我们之前的栈就会不起作用,因为二元组也是一个模板类,之前的栈不支持模板类作为类型参数,可以对栈进行改造,使其能接收二元组模板类,在这基础上,可以写一个新的模板类,把栈和二元组集成进去就可以,这个类接受两个模板类为对像,还可以写一个另外的模板类,接受一个栈模板类,在这个模板类中实例化两个模板栈类对象,代表二元组。
第二种办法看似麻烦,但是他的通用性可能会更好一点,第三种方法既简单又方便实现
这里我们用最后一种形式实现
很明显,我们可以发现,我们无法实现top这个函数,我们无法返回两个元素,若返回只能返回一个结构体,或者改变top的做法。但用其他方法可直接返回一个pair,因为pair模板类是直接压入栈中的。
模板类和友元
如同类可以声明友元一样,模板类也可以声明友元函数。模板的友元分为三类。
1.非模板友元
2.约束模板友元
即友元的类型取决于类被实例化时的类型
3.非约束模板友元
即友元的所有具体化都是类的每一个具体化的友元
读不懂不重要,理解了才重要。
1.非模板友元
在模板中声明一个常规的函数做友元
正因为他不是模板函数,所以模板函数的每一种实例化都要单独写一个友元函数出来
我的友元是我的友元,你的友元是你的友元,虽然我们像兄弟一样都是从模板类具体化出来的,虽然我们很像,但我的朋友不是我兄弟的朋友,我的友元不是我兄弟的友元——模板类如是说道。
可以这样吗
当然不行,因为不存在A这样的对象,只有特定的具体化才能这样做
我们可以这样
因为func并不是类方法,而是友元函数,他不属某个类,也不是模板函数,所以在使用时要显式具体化,在编译时期,我们假设用户传入int类型,那么编译器就会把T换为int,对于func来说,这就是个显式具体化。
重载版本
一个小示例
别忘了初始化静态变量哦!
运行结果
由此也可见A<int>,A<double> ,是两个类!!!并且他们是出自一个模子的。
2.约束模板友元
因为<int>, <double>是不同的类,所以应该让类的每一种具体化都获得一个与友元匹配的具体化
说白了就是模板函数做友元,正好对应上面的如是说道。
可以修改前一个示例,使友元函数本身称为模板
包含以下三补
首先在类前面定义每个友元函数的模板
然后,在模板类中再次将函数声明为友元
声明中的<>指出模板具体化。对于show,可以为空,因为可以从函数参数推断模板类型参数
相当于
最后再像定义模板函数那样定义友元函数即可。
这就正好是模板函数的具体化。
最好将友元的Type 和模板类的Type区分开
正如程序所见,两个counts报告的大小不同,证明了每个类型都有他自己的友元函数。
正对应每个counts的具体化。
3.非约束模板友元
很简单
类外只需要将声明的分号去掉,friend去掉,接花括号定义即可。
模板别名
为了简化代码,可能会用typedef为模板类起一个别名,在C++11中也有类似的功能
arrtype就是std::array<T,12>的别名了。功能比typedef更加强大一些。
using 还可以用于非模板类。当用于非模板时,这种语法与typedef等价
最后,关于具体化和实例化的一些说明
类定义(实例化)在声明类对象并指定特定类型时生成。例如,下main的声明将导致编译器声成类声明,用声明中的实际类型sotr替换模板类中的T
这里,类名为IC <sotr>,而不是IC,IC<sotr>被称为模板具体化
在这种情况下,编译器将使用通用模板生成一个int具体化——CI<int>,虽然尚未请求这个类的对象
可提供显式具体化——覆盖模板类定义的具体类声明,假如模板类不能很好的处理一个字符数组时,我们可以显式具体化一个
方法时template <>打头(就是语法),,然后是模板类名称,在加上尖括号(其中包含要具体化的类型)。
这样,就可以获得专门处理字符数组的模板,而不是再用通用模板类方法,和模板函数的具体化也很像。