//第十三章 类的继承
一.继承
1. 一个简单的基类
`class TableTennisPlayer //作为被继承的基类
{
private:
enum {length = 20};
char name[length];
bool hasTable;
public:
TableTennisPlayer(const char *cname = "none",bool ht = false);
TableTennisPlayer(const TableTennisPlayer & ttp);
~TableTennisPlayer(){}
virtual void show()const;//声明为虚函数
bool HasTable(){return hasTable;}
void ResetTable(bool r){hasTable = r;}
};
//公有继承
class RatedPlayer:public TableTennisPlayer
{
private:
unsigned int rating;
public:
RatedPlayer(unsigned int r,const char *cname = "none",bool ht = false);
RatedPlayer(unsigned int r,const TableTennisPlayer & tp);
~RatedPlayer(){}
virtual void show()const{};//派生类 可以不用加 virtual,自动为虚函数,加上只是为了方便程序员理解
unsigned int Rating(){return rating;}
void ResetRating(unsigned int r){rating = r;}
};
//RatedPlayer 公有继承了 TableTennisPlayer 类`
(1)构造函数:访问权限
RatedPlayer 类可以访问 TableTennisPlayer 的公有成员,比如show()
//在创建派生类对象时,程序首先创建基类对象。所以基类的初始化必须用列表句法实现
RatedPlayer::RatedPlayer(unsigned int r,const char *cname,bool ht):TableTennisPlayer(cname,ht){
//调用了基类构造函数
rating = r; //const 成员函数也必须是用列表句法初始化
}
//如果不调用基类构造函数,程序将使用默认构造函数。
//此列中默认构造函数为 TableTennisPlayer(const char *cname = “none”,bool ht = false)
(2) 调用例子
RatedPlayer rplayer1(10,"wang",true);
TableTennisPlayer & any1 = rplayer1;//基类指针指向 派生类
cout << "any1[0]: \n";
any1.show();//此时调用的是 RatedPlayer::show();
//若基类中的 TableTennisPlayer:show();没有定义为虚函数,则调用 TableTennisPlayer::show();
//由于any1是一个基类指针,采用静态联编`
(3) //总结
//要点:
//(1) 基类对象首先被创建
//(2) 派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数
//(3) 派生类构造函数应初始化派生类新增数据成员
//注意:派生类构造函数总是用于初始化新增数据成员,派生类构造函数总是调用一个基类构造函数。
2. 派生类和基类之间的特殊关系
(1) //公有派生类可以调用 基类 公有成员。
(2) //派生类地址可以赋值给基类,比如 TableTennisPlayer & player0 = rpalyer0; 或者 *player0 = &rpalyer0
二.virtual 的使用
//当派生类有成员函数与基类成员函数名称相同时 声明为虚函数
1.有关虚函数注意事项
(1) 在基类方法声明中使用virtual,可使该方法在基类以及所有派生类中是虚拟的。
(2) 如果使用指向对象的引用或指针来调用虚方法,程序将使用对象类型定义的方法,而不是使用为指针和引用类型定义的方法,
这称为动态联编或晚期联编。
(3)如果定义的类将被用作基类,则应将那些要在派生类中重新定义的类放法声明为虚拟的。
(4)虚函数以及纯虚函数都不能用static定义
2.//构造函数与有缘函数不能为虚函数
3.声明时用virtual定义时不能加virtual
void TableTennisPlayer::show()const{//定义 TableTennisPlayer的show,没有加virtual
std::cout << "The player's name is " << name << ". " << "He hasTable is " << hasTable << ".\n";
}
void RatedPlayer::show()const{//定义 RatedPlayer的show,没有加virtual
TableTennisPlayer::show();
std::cout << "rating = " << rating << ".\n";
}
三.抽象基类
1.//有纯虚函数的类只能作为抽象基类,不能作为类来使用,而只含有虚函数的类不能被称为抽象类
//比如 BaseEllipse a; 这是无效的
class BaseEllipse //抽象基类
{
private:
double x;
double y;
public:
BaseEllipse(double x0=0,double y0 = 0):x(x0),y(y0){}
virtual ~BaseEllipse(){}
void Move(int nx,int ny){x = nx;y = ny;}
virtual double Area()const=0;//纯虚函数,在派生类中被重载,作为多态的基础
}`
2.//派生类举例
class Ellipse: public BaseEllipse
{
private:
double a;
double b;
double angle;
public:
virtual double Area()const{return 3.14*a*b;}//重载纯虚函数
...
}
class Circle : public BaseEllipse
{
private:
...
public:
virtual double Area()const{return 3.14*r*r;}//重载纯虚函数
}
//通过以上两个例子得出纯虚函数的作用:当多个派生类都需要同一函数方法时,单内部实现方法不同时,定义为纯虚函数。
//实际上纯虚函数和虚函数几乎一样,只不过虚函数的基类可以实例化,且有实现方法,而纯虚函数不能被实例化,没有定义实现方法
3. 实例
{
BaseEllipse *base[2]; //虽然抽象基类不能被实例化,但是可以定义成指针
Ellipse ellipse(1,2,1,1);
Circle circle(2,1,1);
base[0] = &ellipse;//通过基类指针,可以管理派生类,多态
base[1] = &circle;
base[0]->show(); //show为虚函数,用法上面已说明
base[1]->show();
}