第13章 类继承

<c++ primer plus>第六版

13 类继承

  1. 面向对象编程的主要目的之一是提供可重用的代码.
  2. 传统的C函数库通过预定义/预编译的函数(strlen(), rand()等)提供可重用性. 但函数库也有局限, 就是无法根据特定需求对函数进行扩展和修改.
  3. c++类提供了更高层次的重用性, 可以通过类继承对类进行扩展和修改.

13.1 一个简单的基类

TableTennisPlayer类

.h文件, 用于定义class, 函数原型等

//tabtenn0.h -- a table tennis base class

#ifndef TABTENN0_H_
#define TABTENN0_H_

#include <string>

using std::string;

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;
        };

};

#endif

.cpp文件用于定义class中的函数

//tabtenn0.cpp -- simple base class methods

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

//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;
}

void TableTennisPlayer::Name() const
{
    std::cout << lastname << ", " << firstname;
}

使用TableTennisPlayer这个类

// usett0.cpp -- using a base class
#include <iostream>
#include "tabtenn0.h" //注意这里只include .h文件, 不include .cpp文件, 只需要class定义及函数原型即可.

int main(void)
{
    using std::cout;
    TableTennisPlayer p1("Chuck", "Blizzard", true);
    TableTennisPlayer p2("Tara", "Boomdea", false);

    p1.Name();
    if (p1.HasTable()) 
        cout << ": has a table.\n";
    else
        cout << ": hasn't a table.\n";

    p2.Name();
    if (p2.HasTable()) 
        cout << ": has a table.\n";
    else
        cout << ": hasn't a table.\n";

    return 0;
}

编译+运行

g++ usett0.cpp tabtenn0.cpp -o usett0.exe #注意, 编译文件列表中没有.h文件
usett0.exe

13.1.1 派生一个类

//以TableTennisPlayer为基类, 派生一个类RatedPlayer
class RatedPlayer : public TableTennisPlayer
{
private:
    unsigned int rating; //add a data member
public:
    RatedPlayer(
        unsigned int r=0,
        const string &fn="none",
        const string &ln="none",
        bool ht=false
    );
    RatedPlayer(
        unsigned int r=0,
        const TableTennisPlayer &tp
    );

    unsigned int Rating() const{ //add a method
        return rating;
    }
    void ResetRating(unsigned int r){ //add a method
        rating=r;
    }
}

注意: 构造函数必须给新成员(如果有的话)和继承的成员提供数据.

13.1.2 构造函数: 访问权限

派生类不能直接访问基类的私有成员, 需要通过基类方法来访问.
所以一般来说, 派生类的构造函数必须使用基类的构造函数.

创建派生类对象时, 程序产生创建基类对象. c++使用成员初始化列表语法来完成这种工作.
例: 一个派生类的构造函数

RatedPlayer::RatedPlayer
(
    unsigned int r,
    const string &fn,
    const string &ln,
    boot ht
): TableTennisPlayer(fn, ln, ht) //在成员初始化列表中调用基类的构造函数.
{
    rating = r;
}

如果上述构造函数中没有显式地调用基类的构造函数, 则将使用基类的默认构造函数.
即:

RatedPlayer::RatedPlayer
(
    unsigned int r,
    const string &fn,
    const string &ln,
    boot ht
) //在成员初始化列表中没显式调用基类的构造函数.
{
    rating = r;
}

等价于:

RatedPlayer::RatedPlayer
(
    unsigned int r,
    const string &fn,
    const string &ln,
    boot ht
): TableTennisPlayer() //等价于使用默认构造函数
{
    rating = r;
}

对于如下代码

RatedPlayer::RatedPlayer
(
    unsigned int r,
    const TableTennisPlayer &tp
): TableTennisPlayer(tp) //传给基类的变量tp的类型是TableTennisPlayer&, 所以将调用基类的复制构造函数.
{
    rating = r;
}

13.1.3 使用派生类

13.1.4 派生类和基类之间的特殊关系

  1. 派生类可以使用基类的方法(前提是该方法不能是私有方法).
  2. 基类指针可以在不进行显式类型转换的情况下指向派生类对象(但只能访问基类的方法).
  3. 基类引用可以在不进行显式类型转换的情况下引用派生类对象.
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
TableTennisPlayer & rt = = rplayer;  //基类引用, 引用派生类对象
TableTennisPlayer * pt = = &rplayer; //基类指针, 指向派生类对象
  1. 如果一个函数的参数是基类引用(或基类指针), 则这个函数也可以接受派生类引用(或派生类指针)作为参数.
  2. 可以将基类对象初始化为派生类对象.
RatedPlayer olaf1(...); //派生类对象
TableTennisPlayer olaf2(olaf1); //用派生类对象 对 基类对象 进行初始化.
  1. 将派生对象赋值给基类对象, 这种情况下程序将使用隐式重载赋值运算符.
RatedPlayer olaf1(...); //派生类对象
TableTennisPlayer winner; //基类对象.
winner = olaf1; //将派生对象赋值给基类对象, 将使用隐式重载赋值运算符:
                //TableTennisPlayer &operator=(const TableTennisPlayer &) const

13.2 继承: is-a关系

c++有3种继承方式: 公有继承, 保护继承, 私有继承.
其中, 公有继承是最常用的方式, 它建立一种is-a关系, 即派生类对象也是一个基类对象, 如果一个操作可以在基类对象上执行, 则这个操作也可以在派生对象上执行.

posted @ 2022-07-10 14:28  编程驴子  阅读(21)  评论(0编辑  收藏  举报