1.继承关系举例
交通工具的分类如下图所示:
这个分类树反映了交通工具的派生关系,最高层是抽象程度最高的,是最具有普遍和一般意义的概念,下层具有了上层的特性,同时加入了自己的新特征,而最下层是最为具体的。这个层次结构中,由上到下,是一个具体化、特殊化的过程;由下到上,是一个抽象化的过程。上下层之间的关系就可以看作是基类与派生类的关系。
所谓继承就是从先辈处得到属性和行为特征。类的继承,就是新的类从已有类那里得到已有的特性。 从另一个角度来看这个问题,从已有类产生新类的过程就是类的派生。 类的继承与派生机制允许程序员在保持原有类特性的基础上,进行更具体、更详细的修改和扩充。由原有的类产生新类时,新类便包含了原有类的特征,同时也可以加入自己所特有的新特性。原有的类称为基类或父类,产生的新类称为派生类或子类。 派生类同样也可以作为基类派生新的类,这样就形成了类的层次结构。类的派生实际上是一种演化、发展过程,即通过扩展、更改和特殊化,从一个已知类出发建立一个新类。通过类的派生可以建立具有共同关键特征的对象家族,从而实现代码的重用,这种继承和派生的机制对于已有程序的发展和改进非常有利。
2.派生类的定义
(1)派生类的一般定义语法
在C++中,派生类的一般定义语法为:
class 派生类名::继承方式 基类名1,继承方式 基类名2,...,继承方式 基类名n
{派生类成员声明;
};
例如,假设基类B1和B2是已经定义的类,下面的语句定义了一个名为D的派生类,该类从基类B1和B2派生而来:
class D::public B1,private B2
{public:D();~D();
};
定义中的“基类名”(如B1和B2)是已有的类的名称,“派生类名”是继承原有类的特性而生成的新类的名称(如D)。
(2)多继承和单继承
一个派生类,可以同时有多个基类,这种情况称为多继承,这时的派生类同时得到了多个已有类的特征。上述例子就是一个多继承实例。一个派生类只有一个直接基类的情况,称为单继承。单继承可以看作是多继承的一个最简单的特例,多继承可以看作是多个单继承的组合,它们之间的很多特性是相同的。两种继承的UML表示如图所示:
(3)直接基类和间接基类
在派生过程中,派生出来的新类也同样可以作为基类再继续派生新的类,此外,一个基类可以同时派生处多个派生类,也就是说,一个类从父类继承来的特征也可以被其他新的类所继承,一个父类的特征,可以同时被多个子类继承。这样,就形成了一个相互关联的类的家族,有时也称作类族。在类族中,直接参与派生出某类的基类称为直接基类,基类的基类甚至更高层的基类称为间接基类。比如由“交通工具”类派生出“汽车”类,“汽车”类又派生出“卡车”类,则“汽车”类是“卡车”类的直接基类,“交通工具”类是“汽车”类的直接基类,而“交通工具”类是“卡车”类的间接基类。
(4)继承方式
在派生类的定义中,除了要指定基类外,还需要指定继承方式。继承方式规定了如何访问从基类继承的成员。。在派生类的定义语句中,每一个“继承方式”,只限定紧随其后的基类。继承方式关键字为:public,protected和private,分别表示公有继承。保护继承和私有继承。如果不显式地给出继承方式关键字,系统的默认值就认为是私有继承(private)。类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。在下面这个例子中:
class D::public B1,private B2
{public:D();~D();
};
对B1是公有继承,对B2是私有继承,同时声明了派生类自己新的构造函数和析构函数。
(5)派生类成员
派生类成员是指除了从基类继承来的所有成员之外,新增加的数据和函数成员。 这些新增的成员,正是派生类不同于基类的关键所在,是派生类对基类的发展。当重用和扩充已有的代码时,就是通过在派生类中新增成员来添加新的属性和功能。
3.派生类生成过程
在C++程序设计中,进行派生类的定义之后,给出该类的成员函数的实现,整个类就算完成了,可以由它来生成对象进行实际问题的处理。
派生新类,实际经历了3个步骤:吸收基类成员,改造基类成员、添加基类成员。
面向对象的继承和派生机制,其最主要目的是实现代码的重用与扩充。因此吸收基类成员就是一个重用的过程,而对基类成员进行调整、改造以及添加新成员就是原有代码的扩充过程,二者是相辅相成的。
基类A和派生类B的定义如下:
class A
{
private:string i;double b;static int t;
protected:A();void r();void e()const;
public:const string &gi()const{}double gb()const{}static double gt(){}void show()const;
};class B :public A
{
private:A a;double c;double r;double q;double gd()const;
public:B();double gc()const;double gr()const;double gq()const;double ga();void de();void wi();void se();void show()const;
};
(1)吸收基类成员
在C++继承中,第一步是将基类的成员全盘接收,这样,派生类实际上就包含了它的全部基类中除构造函数和析构函数之外的所有成员。**在派生过程中构造函数和析构函数都不被继承。**在这里,派生类B继承了基类A中除构造函数和析构函数之外的所有非静态成员:i,b,r函数,e函数,gi函数,gb函数,show函数。经过派生过程,这些成员便存在于派生类之中。
(2)改造基类成员
对基类成员的改造包括两个方面:一个是基类成员的访问控制问题,主要依靠派生类定义时的继承方式来控制。另一个是对基类数据或函数成员的覆盖或隐藏,隐藏就是简单地在派生类中声明一个和基类数据或函数同名的成员,例如上例中的show函数。如果派生类声明了一个和某基类成员同名的新成员(如果是函数成员,则参数表也要相同,参数不同的情况属于重载),派生的新成员就隐藏了外层同名成员。 这时派生类中或者通过派生类的对象,直接使用成员名就只能访问到派生类中声明的同名成员,这称为同名隐藏。在上例中,派生类B中的show函数就隐藏了基类A中的同名函数。
(3)添加新的成员
派生类新成员的加入是继承与派生的核心,是保证派生类在功能上有所发展的关键。可以根据实际情况的需要,给派生类添加适当的数据和函数成员,来实现必要的新增功能。在上例,派生类B中就添加了数据成员a,c,r,q。
由于在派生过程中,基类的构造函数和析构函数不能被继承,因此要实现一些特别的初始化和扫尾清理工作,就需要在派生类中加入新的构造函数和析构函数。例如派生类B中的构造函数B()。