c++primer plus第十四章-多重继承、模板类
1)虚基类
虚基类使得从多个类(它们的基类相同)派生出的对象只继承一个基类对象。
class Singer : virtual public Worker {... ...}
class Waiter : public virtual Worker {... ...}
class SingingWaiter : public Singer , public Waiter {... ...};
SingingWaiter对象都只包含Worker对象的一个副本。继承的Singer和Waiter对象共享一个Worker对象,而不是各自引入自己的Worker对象副本。
2)如果不希望默认构造函数来构造虚基类对象,则需要显式地调用所需的基类构造函数。因此,构造函数应该这样:
SingingWaiter(const Worker & wk, int p=0, int v=Singer :: other) : Worker(wk), Waiter(wk, p), Singer (wk, v){...}
这将显式调用构造函数worker(const Worker &)。对于虚基类,必须这样做,对于非虚基类,则是非法的。
如果类有间接虚基类,则除非只需使用该虚基类的默认构造函数,否则必须显式地调用该虚基类的某个构造函数。
3)虚基类:主要变化(同时也是使用虚基类的原因)是,从虚基类的一个或多个实例派生而来的类将只继承了一个基类对象。为实现这种特性,必须满足其他要求:
第一:有间接虚基类的派生类包含直接调用间接基类构造函数的构造函数,这对于间接非虚基类来说是非法的。
第二:通过优先规则解决名称二义性。
4)类模板
template <class Type>
关键字template告诉编译器,将要定义一个模板。尖括号中的内容相当于函数的参数列表。可以把关键字class看作是变量的类型名,该变量接受类型作为其值,把Type看作是该变量的名称。
这里使用class并不是意味着Type必须是一个类;而只是表面Type是一个通用的类型说明符,在使用模板时,将使用实际的类型替换它。
也可以这样:
template <typename Type>
可以使用自己的泛型名代替Type,当目标被调用时,Type将被具体的类型值取代。
5)可以使用模板成员函数替换原来类的类方法,每个函数头都将以相同的模板声明打头:
bool Stack :: push(const Item & item)
{... ...}
应该为:
template<class Type>
bool Stack<Type> :: push(const Type & item)
{... ...}
6)使用模板类
泛型标识符Type称为类型参数,这意味着它们类似于变量,但是赋给它们的不能是数字,而只能是类型。必须显式地提供所需的类型,这与常规的函数模板不同,因为编译器可以根据函数的参数类型来确定要生成哪种函数。
7)使用指针栈的方法之一是,让调用程序提供一个指针数组,其中每个指针都指向不同字符串,把这些指针放在栈中是有意义的。因为每个指针都将指向不同的字符串。注意,创建不同指针是调用程序的职责,而不是栈的职责。栈的任务是管理指针,而不是创建指针。
8)递归使用模板
ArrayTP<ArrayTP<int, 5>, 10> twodee;
这使得twodee是一个包含10个元素的数组,其中每个元素都是一个包含5个int元素的数组,与之等价的常规数组时:
int twodee[10][5];
9)默认类型模板参数
template<class T1, class T2 = int>
class Topo
{... ...}
如果省略了T2的值,编译器将使用int
Topo<double , double> m1;
Topo<double> m2;//T1 is double, T1 is int
虽然可以为类模板类型参数提供默认值,但不能为函数模板参数提供默认值。然而,可以为非类型参数提供默认值。
10)模板的隐式实例化
即它们声明一个或多个对象,指出所需的类型,而编译器使用通用模板提供的处方生成具体的类定义。
ArrayTP<int, 100> stuff; //隐式定义
编译器在需要对象之前,不会生成类的隐式实例化;
ArrayTP<double, 30> *pt; //a pointer, no object needed yet
pt = new ArrayTP<double , 30>; //now , an object is needed
11)模板的显示实例化
当使用关键字template并指出所需类型来声明类时,编译器将生成类声明的显示实例化。声明必须位于模板定义所在的名称空间中。
template class ArrayTP<string ,100>; //generate ArrayTP<string,100> class
在这种情况下,虽然没有创建或提及类对象,编译器也将生成类声明(包括方法定义)。
12)模板的显式具体化
是特定类型(用于替换模板中的泛型)的定义。有时候,需要在为特殊类型实例化时,对模板进行修改,使其行为不同。
template <typename T>
class SortedArray
{... ...}
template<> class SortedArray<const char char *>
{... ...}
13)模板的部分具体化
即部分限制模板的通用性。部分具体化可以给类型参数之一指定具体的类型:
template <class T1, class T2>
class Pair<T1, int>
{... ...}//具体化了T2。
14)如果有多个模板可供选择,编译器将使用具体化程度最高的模板。
也可以通过为指针提供特殊版本来部分具体化现有的模板:
template<class T>
class Feeb {... ...};
template<class T*>
class Feeb {... ...};
如果提供的类型不是指针,则编译器将使用普通版本;如果提供的是指针,则编译器将使用指针具体化版本;
Feeb<char> fb1;
Feeb<char *> fb2;
如果没有进行部分具体化,则第二个声明将使用通用版本,将T转换为char * 类型。如果进行了部分具体化,则第二个声明将使用具体化版本,将T转换为char。
15)部分具体化可以设置各种限制
//generate template
template <class T1, class T2, class T3> class Trio{...};
//specialization with T3 set to T2
template <class T1, class T2 > class Trio<T1, T2, T3>{...};
//specialization with T3 and T2 set to T1*
template <class T1> class Trio<T1, T1* , T1*>{...};
给定上述声明,编译器将作出如下选择:
Trio<int , short, char *> t1;// use general template
Trio<int , short> t2;// use Trio<T1, T2, T3>
Trio<char, char *, char *> t3;// use Trio<T1, T1* , T1*>