通过继承完成的工作:
可以在已有类的基础上添加新功能;
可以给类添加数据;
可以修改类方法的行为。
例1:string类中有一个将const char *作为参数的构造函数,使得C-风格字符串初始化string类对象时,将自动调用这个构造函数,在头文件中包含string类即可。
公有派生:派生类对象包含基类对象
使用公有派生,基类的私有成员将成为派生类的公有成员;基类的私有部分也成为派生类的一部分,但只能通过基类的公有和保护方法访问。
派生类对象存储了基类的数据成员(即继承了基类的实现)
派生类对象可以使用基类的方法(即继承了基类的接口)
derived::derived(type1 x, type2 y) : base(x,y)
{
….
}
有关派生类构造函数:
首先创建基类对象;
派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;
派生类构造函数应初始化派生类新增的数据成员。
释放对象的顺序与创建对象的顺序相反,首先执行派生类的析构函数,再调用基类的构造函数。
派生类与基类之间的特殊关系:
派生类对象可以使用基类方法,条件是方法不是私有的;
基类指针可以在不进行显示类型转换的情况下指向派生类对象;
基类引用可以在不进行显示类型转换的情况下引用派生类对象;
注意:
基类指针或引用只能调用基类方法
C++要求引用和指针类型与赋给的类型匹配,但这一规则,对继承来说是例外。但这种例外是单向的,不可将基类对象和地址赋给派生类引用和指针。
三种继承方式:
公有继承
保护继承
私有继承
1.公有继承
最常用,建立的是is-a关系,即派生类对象也是一个基类对象,可以对基类对象执行的任何操作,都可以对派生类对象执行。(is-a-kind-of更为准确)
公有继承不建立has-a关系
公有继承不能建立is-like-a关系。继承可以在类的基础上添加一些属性,但不能删除基类的属性。
公有继承不建立is-implemented-as-a(作为…来实现)关系。eg:可以用数组来实现栈,但不能从Array类派生出Stack类。
公有继承不建立uses-a关系。
(1)多态公有继承
实现方式
在派生类中重新定义基类的方法
使用虚方法
注意:
若要在派生类中重新定义基类的方法,通常应将基类方法声明为虚的。这样程序将根据数据类型而不是引用或者指针选择方法版本。为基类声明一个虚析构函数也是一个惯例。
派生类构造函数在初始化基类私有数据时,可采用成员初始化列表。
非构造函数不能使用成员初始化列表语法,但派生类方法可以调用公有的基类方法(使用作用域解析符)。
虚析构函数,有时候会很有必要。
(2)函数名联编:将源代码中的函数调用解释为执行特定的函数代码块。
静态联编(早期联编):编译过程中进行联编;
动态联编(晚期):使用虚函数时,使用哪个函数在编译时期不能确定,对象类型无法选择,编译器必须生成能够在程序运行时选择正确的虚方法的代码。
动态联编的必要性:按值传递会引发隐式向上强制转换,隐式向上强制转换或引用可以指向基类对象或派生类对象。
编译器对虚方法使用动态联编,
动态联编比较好,因为它让程序能够选择为特定类型设计的方法。
但是基于效率和概念模型的考虑,并没有将静态联编摒弃。
效率:静态联编效率更高,为C++的默认选择。
概念模型:如果要在派生类中重新定义基类的方法,则将它设置为虚方法;否则,设置为非虚方法。