本章内容包括:
- is-a关系继承
- 公有派生
- 保护访问
- 构造函数成员初始化列表
- 向上和向下强制转换
- 虚成员函数
- 早起(静态)联编和晚期(动态)联编
- 抽象基类
- 纯虚函数
- 何时及如何使用公有继承
C++提供了比修改代码更好的方法来扩展和修改类。
类继承能够从已有的类派生出新类,派生类继承了基类的特征。
通过继承可以完成:
- 添加功能
- 添加数据
- 修改类方法
13.1 简单基类
//程序清单 13.1
#pragma once
#ifndef TABTENN0_H_
#define TABTENN0_H_
#include <iostream>
#include <string>
using namespace std;
//基类
class TableTennisPlayer
{
public:
TableTennisPlayer(const string& fn = "none", const string& ln = "none", bool ht = false );
~TableTennisPlayer();
void Name() const;
bool HasTable() const { return hasTable; }
void ResetTable(bool v) { hasTable = v; }
private:
string firstname;
string lastname;
bool hasTable;
};
#endif // !TABTENN0_H_
//程序清单 13.2
#include "tabtenn0.h"
TableTennisPlayer::TableTennisPlayer(const string& fn, const string& ln, bool ht)
: firstname(fn), lastname(ln), hasTable(ht) {} //成员初始化列表
/*
TableTennisPlayer::TableTennisPlayer(const string& fn, const string& ln, bool ht)
{
firstname = fn;
lastname = ln;
hasTable = ht;
}
*/
TableTennisPlayer::~TableTennisPlayer()
{
}
void TableTennisPlayer::Name() const
{
cout << lastname << ", " << firstname;
}
//程序清单 13.3
#include <iostream>
#include "tabtenn0.h"
int main(void)
{
TableTennisPlayer player1("Chuck", "Blizzard", true);
TableTennisPlayer player2("Tara", "Boomdea", false);
player1.Name();
if (player1.HasTable())
cout << ": has a table.\n";
else
cout << ": hasn't a table.\n";
player2.Name();
if (player2.HasTable())
cout << ": has a table.\n";
else
cout << ": hasn't a table.\n";
return 0;
}
13.1.1 派生一个类
class RatedPlayer : public TableTennisPlayer
派生类 公有 基类
{
...
}
使用公有派生,基类的公有成员将成为派生类的公有成员;基类的私有部分也成为派生的一部分,只能通过基类的公有和保护方法访问
- 派生类对象存储了基类的数据成员(派生类继承了基类的实现)
- 派生类对象可以使用基类的方法(派生类继承了基类的接口)
需要在继承特性中添加什么:
- 派生类需要自己的构造函数
- 派生类可以添加额外的数据成员和成员函数
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) // : TableTennisPlayer()
{
rating = r;
}
- 下述代码调用基类复制构造函数
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp)
: TableTennisPlayer(tp)
{
rating = r;
}
//或
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp)
: TableTennisPlayer(tp), rating(r) {}
派生类构造函数的要点:
- 创建基类对象
- 通过成员初始化列表将基类信息传递给基类构造函数
- 派生类构造函数应初始化派生类新增的数据成员
释放对象的顺序与创建对象的顺序相反,先调用派生类析构,再调用基类析构
成员初始化列表只能用于构造函数
13.1.3 使用派生类
//程序清单 13.4 tabtenn1.h
#pragma once
#ifndef TABTENN1_H_
#define TABTENN1_H_
#include <iostream>
#include <string>
using namespace std;
//基类
class TableTennisPlayer
{
public:
TableTennisPlayer(const string& fn = "none", const string& ln = "none", bool ht = false);
~TableTennisPlayer();
void Name() const;
bool HasTable() const { return hasTable; }
void ResetTable(bool v) { hasTable = v; }
private:
string firstname;
string lastname;
bool hasTable;
};
//派生类
class RatedPlayer : public TableTennisPlayer
{
public:
RatedPlayer(unsigned int r = 0, const string& fn = "none",
const string& ln = "none", bool ht = false);
RatedPlayer(unsigned int r, const TableTennisPlayer& tp);
~RatedPlayer();
unsigned int Rating() { return rating; }
void resetRating(unsigned int r) { rating = r; }
private:
unsigned int rating;
};
#endif // !TABTENN1_H_
//程序清单 13.5
#include "tabtenn1.h"
//基类
TableTennisPlayer::TableTennisPlayer(const string& fn, const string& ln, bool ht)
: firstname(fn), lastname(ln), hasTable(ht) {} //成员初始化列表
/*
TableTennisPlayer::TableTennisPlayer(const string& fn, const string& ln, bool ht)
{
firstname = fn;
lastname = ln;
hasTable = ht;
}
*/
TableTennisPlayer::~TableTennisPlayer()
{
}
void TableTennisPlayer::Name() const
{
cout << lastname << ", " << firstname;
}
//派生类
Ra