一、一个简单的基类
从一个类派生出另一个类时,原始类称为基类,继承类称为派生类。
派生类对象储存了基类的数据成员(派生类继承了基类的实现)。
派生类对象可以使用基类的方法(派生类继承了基类的接口)。
需要在继承特性中添加什么呢?
派生类需要自己的构造函数。
派生类可以根据需要添加额外的数据成员和成员函数。
构造函数:访问权限的考虑
派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问。具体的说,派生类构造函数必须使用基类构造函数。
有关派生类构造函数的要点如下:
首先创建基类对象;
派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;
派生类构造函数应初始化派生类新增的数据成员;
注意:创建派生类对象时,程序首先调用基类构造函数,然后在调用派生类构造函数。基类构造函数负责初始化继承的数据成员;派生类构造函数主要用于初始化新增的数据成员。派生类的构造函数总是调用一个基类构造函数。可以使用初始化列表语法指明要使用的基类构造函数,否则将使用默认的基类构造函数。
派生类对象过期时,程序将首先调用派生类析构函数,然后在调用设计类析构函数。
派生类和基类之间的特殊关系
派生类与基类之间有一些特殊关系。
其中之一是派生类对象可以使用基类的方法,条件是方法不是私有的。
另外两个重要的关系是:基类指针可以在不进行显式类型转换的情况下指向派生类对象;基类引用可以在不进行显式类型转换的情况下引用派生类对象;
继承is-a关系
派生类和基类之间的特殊关系是基于C++继承的底层模型的。实际上,C++有3中继承方式:公有继承、保护继承和私有继承。
公有继承是最常用的方式,它建立一种is-a的关系,即派生类对象也是一个基类对象,可以对基类对象执行的任何操作,也可以对派生类对象执行。
多态公有继承
在派生类中重新定义基类的方法;
使用虚方法。
注意:如果要再派生类中重新定义基类的方法,通常应将基类方法声明为虚的。这样,程序将根据对象类型而不是引用或指针的类型来选择方法版本。为基类声明一个虚析构函数 也是一种惯例。
静态联编和动态联编
程序调用函数时,将使用哪个可执行代码块呢?编译器负责回答这个问题。将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编。
提示:如果要在派生类中重新定义基类的方法,则将它设置为虚方法;否则设置为非虚方法。
使用虚函数时,在内存和执行速度方面有一定的成本,包括:
每个对象都将增大,增大量为储存地址的空间;
对于每个类,编译器都创建一个虚函数地址表(数组)。
对于每个函数调用,都需要执行一项额外的操作,即到表中查找地址。
虽然非虚函数的效率比虚函数稍高,但不具备动态联编功能。
有关虚函数注意事项
在基类方法的生命中使用关键字virtual可使该方法在基类以及所有的派生类(包括从派生类派生出来的类)中是虚的。
如果使用指向对象的引用或指针来调用虚方法,程序将使用为对象类型定义的方法,而不使用为引用或指针类型定义的方法。这成为动态联编或晚期来那边。这种行为非常重要,因为这样基类指针或引用可以指向派生类对象。
如果定义的类将被用作基类,则应将那些要在派生类中重新定义的类方法声明为虚的。
构造函数
构造函数不能是虚函数
析构函数
析构函数应当是虚函数,除非类不用做基类
给类定义一个虚析构函数并非错误,即使这个类不用做 基类;这只是一个效率方面的问题。
提示:通常应给基类提供一个虚析构函数,即使它并不需要析构函数。
友元
友元不能是虚函数,因为友元不是类成员,而只有成员才能时虚函数。如果由于这个原因引起了设计问题,可以通过让友元函数使用虚成员函数类解决。
没有重新定义
如果派生列没有重新定义函数,将使用该函数的基类版本。如果派生类位于派生链中,则将使用最新的虚函数版本,例外的情况是基类版本是隐藏的。
如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针。这种特性被称为返回类型协变,因为允许返回类型随类类型的变化而变化。
如果基类声明被重载了,则应在派生类中重新定义所有的基类版本。
访问控制:protected
派生类的成员可以直接访问基类的保护成员,但不能直接访问基类的私有成员。
警告:最好对类数据成员采用私有访问控制,不要使用保护访问控制;同时通过基类方法是派生类能够访问基类数据。
抽象基类
继承和动态内存分配
第一种情况:派生类不使用new
第二种情况:派生类使用new
有关使用基类方法的说明:
以公有方式派生的类的对象可以通过多种方式来使用基类的方法。
派生类对象自动使用继承而来的基类方法,如果派生类没有重新定义该方法。
派生列的构造函数自动调用基类的构造函数。
派生类的构造函数自动调用基类的默认构造函数,如果没有再成员初始化列表中指定其他构造函数。
派生类构造函数显式的调用成员初始化列表中指定的基类构造函数。
派生类方法可以使用作用域解析运算符来调用公有的和受保护的基类方法。
派生类的友元函数可以通过强制类型转换,将派生类引用或指针转换为基类引用或指针,然后使用该引用或指针来调用基类的友元函数。