1、继承
从一个类派生出另一个类时,原始类成为基类,继承类称为派生类
class TableTennisPlayer//基类
{
private:
string firstname;
string lastname;
bool hasTable;
public:
TableTennisPlayer(const string & fn = "none",
const string & ln = "none", bool ht = false);
void Name() const;
bool HasTable() const {return hasTable;}
void ResetTable(bool v) {hasTable = v;}
};
class RatedPlayer : public TableTennisPlayer//派生类
{
private:
unsigned int rating;
}
- 派生类对象存储了基类的数据成员(继承了基类的实现)
- 派生类对象可以使用基类的方法(继承了基类的接口)
- 派生类需要自己的构造函数
- 派生类可以根据需要添加额外的数据成员和成员函数
- 派生类构造函数必须给新成员和继承的成员提供数据
- 派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问
- 派生类构造函数必须使用基类构造函数,创建派生类对象时,程序首先创建基类对象,这意味着基类对象应当应当在程序进入派生类构造函数之前被创建,C++使用成员初始化列表语法来完成这种工作
例如:
不调用基类的构造函数,程序将使用默认的基类构造函数,否则应显式调用正确的基类构造函数
RatedPlayer::RatedPlayer(unsigned int r, const string & fn,
const string & ln, bool ht)
调用基类的复制构造函数给基类成员初始化基类成员
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp)
: TableTennisPlayer(tp)
{
rating = r;
}
有关派生类构造函数的要点:
- 首先创建基类对象
- 派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数
- 派生类构造函数应初始化派生类新增的数据成员
- 释放对象的顺序与创建对象的顺序相反,即首先执行派生类的析构函数,然后自动调用基类的析构函数
- 成员初始化列表只能用于构造函数
2、派生类和基类之间的关系
- 派生类对象可以使用基类的方法,条件是方法不是私有的
- 基类指针可以在不进行显示类型转换的情况下指向派生类对象,基类引用可以在不进行显示类型转换的情况下引用派生类对象
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
TableTennisPlayer & rt = rplayer1;
TableTennisPlayer * pt = &rplayer1;
多态公有继承
派生类对象使用基类的方法,而未做任何修改。然而,可能会希望同一个方法在派生类和基类中的行为是不同的。即,方法的行为取决于调用该方法的对象。这种行为成为多态。有两种重要的机制可用于实现多态公有继承:
(1)在派生类中重新定义基类的方法
(2)使用虚方法
(3)如果成员函数是通过引用或指针而不是对象调用的,若没有使用关键字virtual,程序将根据引用类型或指针类型选择方法;如果使用了virtual,程序将根据引用或指针指向的对象的类型来选择方法
//若为非虚函数
Brass dom();
BrassPlus dot();
Brass & b1_ref = dom;
Brass & b2_ref = dot;
b1_ref.ViewAcct();//调用Brass::ViewAcct()
b2_ref.VIewAcct();//调用Brass::ViewAcct()
//若为需函数
b1_ref.ViewAcct();//调用Brass::ViewAcct()
b2_ref.VIewAcct();//调用BrassPlus::ViewAcct()
(4)基类声明了一个虚析构函数。这样做是为了确保释放派身对象时,按正确的顺序调用析构函数
(5)如果要在派生类中重新定义基类的方法,通常应将基类方法声明为虚函数。这样,程序将根据对象类型而不是引用或指针的类型来选择方法版本,为基类声明一个虚析构函数也是一种惯例
(6)若为虚函数,派生类中要调用基类的对应函数,标准技术是使用作用域解析运算符来调用基类方法,若派生类中没有重新定义该方法,不用使用作用域解析运算符
为何需要虚析构函数
class Brass
{
public:
Brass();
virtual ~Brass();
};
class BrassPlus : public Brass
{
public:
BrassPlus();
~BrassPlus();
};
- 使用delete释放由new分配的对象的基类应包含一个虚析构函数。如果析构函数不是虚的,则将只调用对应于指针类型的析构函数。如果析构函数是虚函数,将调用相应对象类型的析构函数。
例如,如果Brass是虚析构函数,如果指针指向的是BrassPlus对象,将调用BrassPlus的析构函数,然后自动调用基类的析构函数。 - 如果BrassPlus(派生类)包含一个执行某些操作的析构函数,则Brass(基类)必须有一个虚析构函数,即使该析构函数不执行任何操作