C++提供了集中重用代码的手段。第13章介绍的共有继承能够建立is-a关系,这样派生类可以重用基类的代码。私有继承和保护继承也使得能够重用基类的代码,单建立的是has-a关系。使用私有继承时,积累的公有成员和保护成员将称为派生类的私有成员;使用保护继承时,积累的公有成员和保护成员将称为派生类的保护成员。无论使用哪种继承,基类的共有接口都将称为派生类的内部接口。这有时候被称为继承实现,但并不继承接口,因为派生类对象不能显式地使用基类的接口。因此,不能将派生类对象看作是一种基类对象。由于这种原因,在不进行显式类型转换的情况下,基类指针或引用将不能指向派生了对象。
还可以通过开发包含对象成员的类来重用类代码。这种发哪个发被称为包含、层次化或组合,它建立的也是has-a关系。与私有继承和保护继承相比,包含更容易实现和使用,所以有限采用这种方式。然而,私有继承和保护及成比包含有一些不同的功能。例如,继承允许派生类访问基类的保护成员;还允许派生类重新定义从基类那里继承的虚函数。因为包含不是继承,所以通过包含来重用类代码时,不能使用这些功能。另一方面,如果需要使用某个类的几个对象,则用包含更适合。例如,State类可以包含一组Country对象。
多重继承(MI)使得能后在类设计中重用多个类的代码。私有MI或保护MI建立has-a关系,而共有MI建立is-a关系。MI会带来一些问题,即多次定义同一个名称,继承多个基类对象。可以使用类限定符来解决名称二义性的问题,使用虚基类来避免继承多个基类对象的问题。单使用虚基类后,就需要为编写构造函数初始化列表以及解决二义性问题引入新的规则。
类模板使得能够创建通用的类设计,其中类型(通常是成员类型)由类型参数表示。典型的模板如下:
template <class T
class Ic
{
T v;
...
public:
Ic(const T & val) : v(val) { }
...
};
其中,T是类型参数,用作以后将指定的实际类型的占位符(这个参数可以是任意有效的C++名称,单通常使用T和Type)。在这种环境下,也可以使用typename代替class:
template <typename T> // same as template <class T>
class Rev {...} ;
类定义(实例化)在声明类对象并指定特定类型时生成。例如,下面的声明导致编译器生成类声明,用声明中的实际类型short替换模板中的所有类型参数T:
class Ic<short> sic; // implicit instantiation
这里,类名为Ic<short>,而不是Ic。Ic<short>称为模板具体化。具体地说,这是一个隐式实例化。
使用关键字template声明类的特定具体化时,将发生显式实例化:
template class Ic<int>; // explicit instantiation
在这种情况下,编译器将使用通用模板生成一个int具体化——Ic<int>,虽然尚未请求者个类的对象。
可以提供显式具体化——覆盖模板定一个具体类声明。方法是以template<>打头,然后是模板类名称,再加上尖括号(其中包含要具体化的类型)。例如,为字符指针提供专用Ic类的代码如下:
template <> class Ic<char *>
{
char * str;
...
public:
Ic(const char * s) : str(s) { }
...
};
这样,下面这样的声明将为chic使用专用定义,而不是通用模板:
class Ic<char *> chic;
类模板可以指定多个泛型,也可以有非类型参数:
template <class T, class TT, int n>
class Pals {...};
下面的声明将生成一个隐式实例化,用double代替T,用string代替TT,用6代替n:
Pals<double, string, 6> mix;
类模板还可以包含本身就是模板的参数:
template < template <typename T> class Cl, typename U, int z >
class Trophy {...};
其中z是一个int值,U为类型名,CL为一个使用template<typename, T>声明的类模板。
类模板可以被部分具体化:
template <class T> Pals<T, T, 10> { ... };
template <class T, class TT> Pals<T, TT, 100> { ... };
template <class T, int n> Pals <T, T*, n> { ... };
第一个声明为两个类型相同,且n的值为6的情况创建了一个具体化。同样,第二个声明为n等于100的情况创建一个具体化;第三个声明为第二个类型是指向第一个类型的指针的情况创建了一个具体化。
模板类可用作其他类、结构和模板的成员。
所有这些机制的目的都是为了让程序员能够重用经过测试的代码,而不用手工复制它们。这样可以简化编程工作,提供程序的可靠性。