本章内容包括:is-a关系的继承;如何以公有方式从一个类派生出另一个类;保护访问;构造函数成员初始化列表;向上和向下强制转换;虚成员函数;早期(静态)联编与晚期(动态)联编;抽象基类;纯虚函数;何时及如何使用公有函数。
C++提供了比修改代码更好的方法来扩展和修改类。这种方法叫作类继承,它能够从已有的类派生出新的类,而派生类继承了原有类(称为基类)的特征,包括方法。
- 可以在已有类的基础上添加功能。例如,对于数组类,可以添加数学运算。
- 可以给类添加数据。例如,对于字符串类,可以派生出一个类,并添加指定字符串显示颜色的数据成员。
- 可以修改类方法的行为。(虚函数、纯虚函数)
继承机制甚至可以不访问源代码就可以派生出类。
13.1 一个简单的基类
从一个类派生出另一个类时,原始类称为基类,继承类称为派生类。
code_c++/C++ Primer Plus(第6版)/Chapter 13 · Kite/C和C++ - 码云 - 开源中国 (gitee.com)
13.1.1 派生一个类
class RatedPlayer : public TableTennisPlayer
- 冒号指出RatedPlayer类(派生类)的基类是TableTennisplayer类。
- public表明TableTennisPlayer是一个公有基类,这被称为公有派生。
- 派生类对象包含基类对象。
- 使用公有派生,基类的公有成员将成为派生类的公有成员;基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问。
- 派生类对象存储了基类的数据成员(派生类继承了基类的实现);
- 派生类对象可以使用基类的方法(派生类继承了基类的接口)。
- 派生类需要自己的构造函数。构造函数必须给新成员(如果有的话)和继承的成员提供数据。
- 派生类可以根据需要添加额外的数据成员和成员函数。
// simple derived class
class RatedPlayer : public TableTennisPlayer
{
private:
unsigned int rating; // add a data member
public:
RatedPlayer (unsigned int r = 0, const string & fn = "none",
const string & ln = "none", bool ht = false); // 构造函数1
RatedPlayer(unsigned int r, const TableTennisPlayer & tp); // 构造函数2
unsigned int Rating() const { return rating; } // add a method
void ResetRating (unsigned int r) {rating = r;} // add a method
};
13.1.2 构造函数:访问权限的考虑
派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问。必须使用基类的公有方法来访问私有的基类成员。派生类构造函数必须使用基类构造函数。
创建派生类对象时,程序首先创建基类对象。从概念上说,这意味着基类对象应当在程序进入派生类构造函数前被创建。
//成员初始化列表
RatedPlayer::RatedPlayer(unsigned int r, const string & fn,
const string & ln, bool ht) : TableTennisPlayer(fn, ln, ht) // 调用基类默认构造函数
{
rating = r;
}
//如果省略成员初始化列表
RatedPlayer::RatedPlayer(unsigned int r, const string & fn,
const string & ln, bool ht) // 调用基类复制构造函数
{
rating = r;
}
//必须首先创建基类对象,如果不调用基类构造函数,那就要使用默认的基类构造函数
//所以上述代码与下述代码等效
RatedPlayer::RatedPlayer(unsigned int r, const string & fn,
const string & ln, bool ht) :TableTennisPlayer()
{
rating = r;
}
//第二个构造函数
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp)
:TableTennisPlayer(tp)
{
rating = r;
}
//这里也会将TableTennisPlayer的信息传递给了TableTennisPlayer构造函数:
//TableTennisPlayer(tp)
如果愿意,也可以对派生类成员使用成员初始化列表语法。在这种情况下,应在列表中使用成员名,而不是类名。所以第二个构造函数可以按照下述方式编写: