Base class(基类)Parent class(父类)Super class(超类)Derived class(派生类)Sub class(子类)
派生类重用基类类的代码可提高程序开发效率。派生类的定义通常基于设计完善、并经严格测试的基类,从而使程序设计工作建立在一个可靠的基础上,有助于高效地开发出可靠性较高的软件
这种重用是一种灵活的重用方式:子类在继承父类代码的基础上,可根据自己的特性进行调整。
在类B中除了自己定义的成员之外,还自动包括了类A中定义的数据成员与成员函数,这些自动继承下来的成员称为类B的继承成员
class 派生类名:继承访问控制 基类类名{
成员访问控制:
成员声明列表;
};
class ExtTime : public Time // Time is the base class
继承访问控制和成员访问控制均由保留public、protected、private来定义,缺省均为private。
private(私有的):
在private后声明的成员称为私有成员,私有成员只能通过本类的成员函数来访问。
public(公有的):
在public后声明的成员称为公有成员,公有成员用于描述一个类与外部世界的接口,类的外部(程序的其它部分的代码)可以访问公有成员。
protected(受保护的):
受保护成员具有private与public的双重角色:对派生类的成员函数而言,它为public,而对类的外部而言,它为private。即:protected成员只能由本类及其后代类的成员函数访
问。
(在大多数情况下,使用public的继承方式;private和protected是很少使用的。
微软的MFC:全部使用public的继承方式
AT&T的iostream库:95%以上使用的是public的继承方式)
继承时的构造函数
基类的构造函数不被继承,派生类中需要声明自己的构造函数。
派生类的构造函数中只需要对本类中新增成员进行初始化即可。对继承来的基类成员的初始化是通过自动调用基类构造函数完成的。
派生类的构造函数需要给基类的构造函数传递参数。
构造函数的调用次序
构造函数的调用次序(创建派生类对象时)
首先调用其基类的构造函数(调用顺序按照基类被继承时的声明顺序(从左向右))。
然后调用本类对象成员的构造函数(调用顺序按照对象成员在类中的声明顺序)。
最后调用本类的构造函数。
析构函数的调用次序(与构造函数相反)
首先调用本类的析构函数
然后调用本类对象成员的析构函数
最后调用其基类的析构函数
向基类构造函数传递实参
若基类构造函数带参数,则定义派生类构造函数时通过初始化列表显式调用基类构造函数,并向基类构造函数传递实参。
ExtTime::ExtTime( int initHrs, nt initMins, int initSecs,ZoneType initZone )
: Time(initHrs, initMins, initSecs)
Avoiding Multiple Inclusion of Header Files
#ifndef Preprocessor_Identifier
#define Preprocessor_Identifier
:
#endif
恢复访问控制方式
基类中的public或protected成员,因使用protected或private继承访问控制而导致在派生类中的访问方式发生改变,可以使用“访问声明”恢复为原来的访问控制方式
class DERIVED: private BASE {
public:
BASE::set_i; // 访问声明
BASE::i;
void set_j(int x){
j = x;
}
int get_ij(){
return i + j;
}
protected:
int j;
};
继承成员重定义
派生类中修改继承成员函数的语义(即,修改函数体,而保持函数原型不变)。
派生类中的名字支配(屏蔽)基类中的名字。
编译器对成员函数调用的处理
利用重载,实现新的功能。
函数重载:函数名相同,函数首部不相同
// IMPLEMENTATION FILE ( time.cpp )
void Time::Set( int hours, int minutes, int seconds )
{
hrs = hours;
mins = minutes;
secs = seconds;
}
// IMPLEMENTATION FILE ( Exttime.cpp )
void ExtTime::Set( int hours, int minutes, int seconds,
ZoneType timeZone )
{
Time::Set(hours, minutes, seconds);
zone = timeZone;
}
函数重定义:函数首部相同,实现不同
/ ( time.cpp )
void Time::Write() const
{.....
}
// ( Exttime.cpp )
void ExtTime::Write() const
{
....}
屏蔽继承成员
目的:
使得客户代码通过派生类对象不能访问继承成员。
方法:
使用继承访问控制protected和private(真正屏蔽)
在派生类中成员访问控制protected或private之后定义与继承成员函数相同的函数原型,而函数体为空(非真正屏蔽,仍可通过使用“基类名::成员名”访问)
继承成员重命名
目的:
解决名字冲突。
在派生类中选择更合适的术语命名继承成员。
方法
在派生类中定义新的函数,该函数调用旧函数;屏蔽旧函数。
在派生类中定义新的函数,该函数的函数体与旧函数相同。
类型兼容性
赋值运算的类型兼容性
可以将后代类的对象赋值给祖先类对象,反之不可。
每个派生类对象包含一个基类部分,这意味着可以将派生类对象当作基类对象使用。
Name clash(名字冲突)
class BASE1 {
public: void show() { cout << i << "\n"; }
protected: int i;
};
class BASE2 {
public: void show() { cout << j << "\n"; }
protected: int j;
};
// 多重继承引起名字冲突:DERIVED的两个基类BASE1和//BASE2有相同的名字show
class DERIVED: public BASE1, public BASE2 {
public:
void set(int x, int y) { i = x; j = y; }
};// 派生类在编译时不出错:C++语法不禁止名字冲突。
int main()
{
DERIVED obj; // 声明一个派生类的对象
obj.set(5, 7); // set()是DERIVED类自身定义的
// obj.show();
// 二义性错误,编译程序无法决定调用哪一个版本
obj.BASE1::show();
// 正确,显式地调用从BASE1继承下来show()
obj.BASE2::show();
// 正确,显式地调用从BASE2继承下来show()
:
}
Two types of double inheritance
Duplicate inheritance(复制继承):There are several copies of the base class members in a derived class object.
Shared inheritance(共享继承):There is only one copy of the base class members in a derived class object.
class BASE {public: int i;};
class BASE1: public BASE {
public: int j;
};
class BASE2: public BASE {
public: int k;
};
class DERIVED: public BASE1, public BASE2 {
public: int sum;
};
void main()
{
DERIVED obj; // 声明一个派生类对象
obj.i = 3; //错误,编译程序无法确定使用i的哪一份副本
obj.j = 5; //正确,使用从BASE1继承下来的j
obj.k = 7; //正确,使用从BASE2继承下来的k
}
Virtual base(虚基类)
普通基类与虚基类之间的唯一区别只有在派生类重复继承了某一基类时才表现出来。
class BASE {public: int i;};
class BASE1: virtual public BASE {
public: int j;
};
class BASE2: virtual public BASE {
public: int k;
};
class DERIVED: public BASE1, public BASE2 {
public: int sum;
};
虚基类的构造函数与析构函数
创建后代类对象时,只有该后代类列出的虚基类构造函数被调用,这样就保证了虚基类的唯一副本只被初始化一次。
创建派生类对象时构造函数的调用次序:
最先调用虚基类的构造函数;
其次调用普通基类的构造函数,多个基类则按派生类声明时列出的次序、从左到右调用,而不是初始化列表中的次序;
再次调用对象成员的构造函数,按类声明中对象成员出现的次序调用,而不是初始化列表中的次序
最后执行派生类的构造函数。