派生类和基类的关系—类继承

时间:2022-05-23 00:28:04
C++之类继承

    一、意义:面向对象编程的主要目的之一是提供可重用的代码,开发新工程,尤其是当工程十分庞大时,重用经过测试的代码比重新编写代码要好的多,使用已有的代码可以节省时间,由于已有的代码已被测试和使用过,因此有助于避免在程序中引入错误。另外,必须考虑的细节越少,便越能专注于程序的整体策略。
    通常,类库是以源代码的方式提供的,这意味着可以对其进行修改,以满足需求,但是,C++提供了比修改代码更好的方法来扩充和修改类。这种方法叫作类继承,它能够从已有的类派生出新的类,而派生类继承了原有类的特征,包括方法。
    通过继承可以完成的工作:
可以在已有类的基础上添加功能。
可以给类添加数据。
可以修改类方法的行为。

二、一个简单的基类
从一个类派生出另一个类时,原始类称为基类,继承类称为派生类,为说明继承,首先需要一个基类。为此,我们举一个例子:乒乓球俱乐部决定跟踪乒乓球会员,需要设计一个简单的TableTennisPlayer类。

如下程序所示:

tabtenn0.h

#ifndef TABTENN0_H_
#define TABTENN0_H_

class TableTennisPlayer
{
private:
enum{LIM = 20};
char firstname[LIM];
char lastname[LIM];
bool hasTable;
public:
TableTennisPlayer(const char * fn = "none",
const char * ln = "none",bool ht = false);
void Name()const;
bool HasTable()const {return hasTable;};
void ResetTable(bool v) {hasTable = v;};
};

#ednif
tabtenn0.cpp
#include "tabtenn0.h"
#include <iostream>
#include <cstring>
TableTennisPlayer::TableTennisPlayer(const char * fn, const char * ln, bool ht)
{
std::strncpy(firstname,fn,LIM - 1);
firstname[LIM -1] = '\0';
std::strncpy(lastname,ln,LIM - 1);
lastname[LIM - 1] = '\0';
hasTable = ht;
}

void TableTennisPlayer::Name()const
{
std::cout << lastname << ", " << firstname;
}
usetabtenn0.cpp--use a base class

#include <iostream>
#include "tabtenn0.h"

int main(void)
{
using std::cout;
    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 (player.2HasTable())        cout << ": has a table";    else        cout << ": hasn't a table.\n";    return 0;}
三、据此我们派生一个类:
    俱乐部的一些成员参加过比赛,需要这样一个类,它能包括成员在比赛中的比分。首先将RatedPlayer类声明为从TableTennisPlayer类派生而来:
    class RatedPlayer: TableTennisPlayer
{

}
分析:
    冒号指出RatedPlayer类的基类是TableTennisPlayer类。上述特殊的声明头表明TableTennisPlayer是一个公有基类,这被称为公有派生。派生类对象包含基类对象。使用公有派生,基类的公有成员将成为派生的公有成员;基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问。
    需要在继承特性中添加什么呢?
1.派生类需要自己的构造函数。
2.派生类可以根据需要添加额外的数据成员和成员函数。
在这个例子中,派生类需要另一个数据成员来存储比分,还应包含检索比分的方法和重置比分的方法,因此,类声明应如下:

    class RatedPlayer: TableTennisPlayer
{
private:
unsigned int rating;
public:
RatedPlayer(unsigned int r=0, const char *fn = "none",
const char * ln = "none", bool ht = false);
RatedPlayer(unsigned int r, const TableTennisPlayer &tp);
unsigned int Rating(){return rating;}
void ResetRating(unsigned int r){rating = r;}
}
    构造函数必须给新成员和继承的成员提供数据。在第一个RatedPlayer构造函数中,每一个成员对应一个形参,而第二个RatedPlayer构造函数使用一个TableTennisPlayer参数,该参数包括firstname、lastname、和hasTable.
四、构造函数:访问权限的考虑
    有关派生类构造函数的要点如下:
1.基类对象首先被创建。
2.派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数。
3.派生类构造函数应初始化派生类新增的数据成员。
五、使用派生类
    要使用派生类,程序必须要能够访问基类声明。下面示例程序将这两种类的声明放在了同一个头文件中,也可以将每个类放在独立的头文件中,但由于这两个类是相关的,所有把其类声明放在一起更合适。

tabtenn1.h
//tabtenn1.h--simple inheritance
#ifndef TABTENN1_H_
#define TABTENN1_H_

class TableTennisPlayer
{
private:
enum{LIM = 20};
char firstname[LIM];
char lastname[LIM];
bool hasTable;
public:
TableTennisPlayer(const char * fn = "none",
const char * ln = "none",bool ht = false);
void Name()const;
bool HasTable()const {return hasTable;};
void ResetTable(bool v) {hasTable = v;};
};

//simple derived class
class RatedPlayer:public TableTennisPlayer
{
private:
unsigned int rating;
public:
RatedPlayer(unsigned int r=0,const char * fn="none",
const char * ln = "none",bool ht= false);
RatedPlayer(unsigned int r,const TableTennisPlayer & tp);
unsigned int Rating(){return rating;}
void ResetRating(unsigned int r){rating = r;}
};

#endif
tabtenn1.cpp
//tabtenn1.cpp--base class methods and derived - class methods
#include "tabtenn1.h"
#include <iostream>
#include <cstring>

//TableTennisPlaye methods
TableTennisPlayer::TableTennisPlayer(const char * fn, const char * ln, bool ht)
{
std::strncpy(firstname,fn,LIM - 1);
firstname[LIM -1] = '\0';
std::strncpy(lastname,ln,LIM - 1);
    lastname[LIM - 1] = '\0';    <span style="font-family: Arial, Helvetica, sans-serif;">hasTable = ht;</span>
}void TableTennisPlayer::Name()const{    std::cout << lastname << ", " << firstname;}//RatedPlayer methodsRatedPlayer::RatedPlayer(unsigned int r, const char * fn,const char * ln,bool ht):TableTennisPlayer(fn,ln,ht){    rating = r;}RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp):TableTennisPlayer(tp),rating(r){    }
usetabtenn1.cpp
//usett1.cpp--using base class and derived class
#include <iostream>
#include "tabtenn1.h"

int main(int argc, char * argv [ ])
{
using std::cout;
using std::endl;
TableTennisPlayer player1("Tara","Boomdea",false);
RatedPlayer rplayer1(1140,"Mallory","Duck",true);
rplayer1.Name();
if (rplayer1.HasTable())
cout << "has a table.\n";
else
cout << ": hasn't a table.\n";
player1.Name();
if (player1.HasTable())
cout << " has a table";
else
cout << ": hasn't a table.\n";
cout << " Name: ";
rplayer1.Name();
cout << ": Rating: " << rplayer1.Rating() << endl;
RatedPlayer rplayer2(1212,player1);
cout << "Name: ";
rplayer2.Name();
cout << ":Rating: " << rplayer2.Rating() << endl;

return 0;
}
注意这两个类对象是如何使用TableTennisPlayer类的Name()和HasTable()方法的。


六、派生类和基类之间的特殊关系
1.派生类对象可以使用基类的方法,条件是方法不是私有的。
2.基类指针可以在不进行显示类型转换的情况下指向派生类对象。
3.基类引用可以在不进行显示类型转换的情况下引用派生类对象。
4.基类指针或引用只能用于调用基类方法。