1、类的继承与派生
类的继承,是新的类从已有类那里得到已有的特性。从已有类产生新类的过程就是类的派生。
原有的类称为基类或父类,产生的新类称为派生类或子类。
(1)派生类的定义
class 派生类名:继承方式 基类名1,继承方式 基类名2,继承方式 ...
{
派生类成员声明;
};
例如: Base1 和 Base2 是已经定义的类,下面是定义一个 名为 Derived 的派生类:
class Derived:public Base1,private Base2
{
public:
Derived();
~Derived();
};
一个派生类,可以同时有多个基类,这种情况称为多继承,只有一个直接基类的情况称为单继承。
继承方式规定了如何访问从基类继承的成员。默认缺省为私有继承(private)。
派生类成员是指除了从基类继承来的所有成员之外,新增加的数据和函数成员。
(2)派生类生成过程
a、吸收基类成员
这是继承的第一步,是将基类的成员全盘接收
这样派生类实际包含了它的基类中除构造函数和析构函数之外的所有成员
b、改造基类成员
对基类成员的改造包括两个方面,一是基类成员的访问控制问题,主要依靠派生类定义时的继承方式来控制
另一个是对基类数据或函数成员的覆盖或隐藏,而隐藏就是简单地在派生类中声明一个和基类数据或函数同
名的成员。如果派生类声明了一个和某基类成员同名的新成员(如果是成员函数,则参数表也要相同,参数不同的情况属
于重载),派生的新成员就隐藏了外层同名成员(称作同名隐藏)。
c、添加新的成员
2、访问控制
(1)公有继承
当类的继承方式为公有继承时,基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问
(2)私有继承
当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,
而基类的私有成员在派生类中不可直接访问。
(3)保护继承
保护继承中,基类的公有成员和保护成员都以保护成员身份出现在派生类中,而基类的私有成员不可直接访问。
3、类型兼容规则
类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代:
(1)派生类的对象可以隐含转换为基类的对象
(2)派生类的对象可以初始化基类的引用
(3)派生类的指针可以隐含转换为基类的指针
在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员,实例:
#includeusing namespace std; class Base1 //基类 Base1 定义 { public: void display() const {cout<<"Base1::display()"< display(); //对象指针->成员名 } int main() { Base1 base1; Base2 base2; Derived derived; fun(&base1); fun(&base2); fun(&derived); return 0; }
运行结果:
vc6qo7o8L3A+CjxwPiAgICAgICAgICAgICAgPHN0cm9uZz7Fycn6wODD+6O6o7rFycn6wODD+6Ooss7K/bHto6mjurv5wODD+zGjqLv5wOAxs/XKvLuvss7K/bHto6mjrC4uLiCjrLv5wODD+26jqLv5wODD+26z9cq8u6+yzsr9se2jqaOsPC9zdHJvbmc+PC9wPgo8cD48c3Ryb25nPrPJ1LG21M/zw/sxo6izydSxttTP8zGz9cq8u6+yzsr9se2jqaOsLi4uIKOss8nUsbbUz/PD+22jqLPJ1LG21M/zbbP1yry7r7LOyv2x7aOpPC9zdHJvbmc+PC9wPgo8cD48c3Ryb25nPiAgICAgICAgICAgICAgezwvc3Ryb25nPjwvcD4KPHA+PHN0cm9uZz4gICAgICAgICAgICAgICAgICAgICAgICDFycn6wOC5udTsuq/K/bXExuTL+7P1yry7r7LZ1/c7PC9zdHJvbmc+PC9wPgo8cD48c3Ryb25nPiAgICAgICAgICAgICAgIH08L3N0cm9uZz48L3A+CjxwPiAgICAgICAgICAgICAgINXiwO+jrMXJyfrA4LXEubnU7Lqvyv3D+9PrwODD+8/gzayhozwvcD4KPHA+ICAgICAgICAgICAgICAgyOe5+7bUu/nA4LP1yry7r8qxo6zQ6NKqtffTw7v5wOC1xLT409DQzrLOse21xLm51Oy6r8r9yrGjrMXJyfrA4L7NsdjQ68n5w/e5udTsuq/K/aGjPC9wPgo8cD4gICAgICAgICAgICAgICDFycn6wOC5udTsuq/K/da00NC1xNK7sOO0ztDyyOfPwqO6PC9wPgo8cD4gICAgICAgICAgICAgICBhoaK199PDu/nA4Lm51Oy6r8r9o6y199PDy7PQ8rC01dXL/MPHsbu8zLPQyrHJ+cP3tcTLs9Dyo6i009fzz/LT0qOpPC9wPgo8cD4gICAgICAgICAgICAgICBioaK21MXJyfrA4NDC1Pa1xLPJ1LG21M/zs/XKvLuvo6y199PDy7PQ8rC01dXL/MPH1NrA4NbQyfnD97XEy7PQ8jwvcD4KPHA+ICAgICAgICAgICAgICAgY6Gi1rTQ0MXJyfrA4LXEubnU7Lqvyv3M5dbQtcTE2sjdPC9wPgo8cD4gICAgICAgICAgICAgICDKtcD9o7rFycn6wOC5udTsuq/K/cq1wP2jqLbgvMyz0KOsuqzT0MTax7a21M/zo6k8L3A+CjxwPjxwcmUgY2xhc3M9"brush:java;">#include
运行结果:

分析:基类构造函数的调用顺序是按照派生类定义时的顺序,为 Base2,Base1,最后Base3
而内嵌对象的构造函数调用顺序应该是按照成员在类中的声明顺序:Base1,Base2,Base3。
(2)拷贝构造函数
一般缺省时系统会在必要时自动生成一个隐含的拷贝构造函数,这个拷贝构造函数会自动调用基类的拷贝构造
函数,然后对派生类新增的成员对象一一执行拷贝。
如果要为派生类编写拷贝构造函数,一般需要为基类相应的拷贝构造函数传递参数。
(3)析构函数
派生类析构函数的声明方法与没有继承关系的类中析构函数的声明方法完全相同,只要在函数体中负责把派生类
新增的非对象成员的清理工作做好就够了。它的执行次序和构造函数正好完全相反。
5、派生类成员的标识与访问
(1)作用域分辨符
作用域分辨符,就是 "::" ,它可以用来限定要访问的成员所在的类的名称。
对于在不同的作用域声明的标识符,可见性原则是:外层声明一个标识符,内层如果未声明,则外层在内层仍然可见,若内层声明
了同名标识符,则隐藏外层同名标识符(称隐藏规则)。
如果派生类中声明了与基类成员函数同名的新函数,即使函数的参数表不同,从基类继承的同名函数的所有重载形式也都会被隐藏。
(2)虚基类
如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。
在引用这些同名的成员时,必须在派生类对象名后增加直接基类名,以避免产生二义性,使其惟一地标识一个成员,如: obj.A::display( )。
在一个类中保留间接共同基类的多份同名成员是不好的,C++提供虚基类(virtual base class )的方法,使得在继承间接共同基类时只保留一份成员。
语法形式: class 派生类名:virtu