• 第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关系, 即派生类对象也是一个基类对象, 如果一个操作可以在基类对象上执行, 则这个操作也可以在派生对象上执行.

  • 相关阅读:
    阿里云oss云存储-----ossutil工具的使用
    Saltstack的安装
    SaltStack自定义modules模块
    Hadoop综合大作业
    理解MapReduce
    熟悉常用的HBase操作
    熟悉常用的HDFS操作
    爬虫大作业
    Hadoop综合大作业
    理解MapReduce
  • 原文地址:https://www.cnblogs.com/gaiqingfeng/p/16463157.html
Copyright © 2020-2023  润新知