类继承,能够从已有的类派生出新的类,而派生类继承了原有类(基类)的特征,包括方法。
class RatedPlayer :public TableTennisPlayer { ... };使用公有派生,基类的公有成员将成为派生类的公有成员;基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问。因此,
派生类对象存储了基类的数据成员(派生类继承了基类的实现);
派生类对象可以使用基类的方法(派生类继承了基类的接口)。
此外,还需要在继承特性中添加,
派生类需要自己的构造函数;派生类可以根据需要添加额外的数据成员和函数。
派生类构造函数
由于派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问。因此,派生类构造函数必须使用基类构造函数。派生类构造函数的要点如下:
首先创建基类对象;
派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;
派生类构造函数应初始化派生类新增的数据成员。
RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht) :TableTennisPlayer(fn, ln, ht) { rating = r; }
上面的构造函数中,TableTennisPlayer(fn, ln, ht)是成员初始化列表。
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp) : TableTennisPlayer(tp), rating(r) { }
由于tp的类型为TableTennisPlayer&,因此将调用基类的复制构造函数。
如果不调用基类的构造函数,程序将使用默认的基类构造函数,所以下面两个构造函数将是等效的。
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 rplayer1(1140, "Mallory", "Duck", true); TableTennisPlayer & rt = rplayer; TableTennisPlayer * pt = &rplayer; rt.Name(); //invoke Name() with reference, Name() is method of base class pt->Name(); //invoke Name() with pointer TableTennisPlayer("Betsy", "Bloop", true); RatedPlayer & rr = player; //not allowed RatedPlayer * pr = player; //not allowed
类似地,形参为基类引用或指针的函数可用于基类对象或派生类对象。
void show(const TableTennisPlayer & rt) { ... } void Wohs(const TableTennisPlayer * pt) { ... } RatedPlayer rplayer(1140, "Mallory", "Duck", true); TableTennisPlayer tplayer("Betsy", "Bloop", true); show(rplayer); Wohs(&rplayer); show(tplayer); Wohs(&tplayer);
此外,引用兼容性也能够将基类对象初始化为派生类对象,将派生对象赋给基类对象。
RatedPlayer rplayer1(1140, "Mallory", "Duck", true); TableTennisPlayer rplayer2(rplayer1); //TableTennisPlayer(const TableTennisPlayer &)
要初始化rplayer2,匹配的构造函数的原型为TableTennisPlayer(const RatedPlayer &); 类定义中没有这样的构造函数,但存在隐式复制构造函数TableTennisPlayer(const TableTennisPlayer &); 这个函数形参是基类引用,可以引用派生类。将rplayer2初始化为rplayer1时,使用此构造函数将rplayer1的基类成员复制给rplayer2。也就是说,它将rplayer2初始化为嵌套在RatedPlayer对象rplayer1中的TableTennisPlayer对象。
RatedPlayer rplayer1(1140, "Mallory", "Duck", true); TableTennisPlayer rplayer2; rplayer2 = rplayer1; //TableTennisPlayer & operator=(const TableTennisPlayer & )const这种情况下,默认构造函数TableTennisPlayer & operator=(const TableTennisPlayer & )const中的基类引用可以指向派生类对象,因此rplayer1的基类部分被复制给rplayer2。