zoukankan      html  css  js  c++  java
  • Cpp Chapter 13: Class Inheritance Part1

    class inheritance lets you derive new classes from old ones, inheriting its properties of the old class, called the base class
    With inheritance, you can:
    1 add functionality to existing classes
    2 add the data a class represents
    3 modify how a class method behaves


    13.1 beginning with a simple base class

    ) When one class inherits from another, the original is called the base class and the inheriting one is called the derived class
    Let's design a class for table tennis players, begining with a base class:

    // tabtenn0.h -- a table-tennis base class
    #ifndef TABTENN0_H_INCLUDED
    #define TABTENN0_H_INCLUDED
    #include <string>
    using std::string;
    
    class TableTennisPlayer
    {
    private:
        string firstname;
        string lastname;
        bool hasTable;
    public:
        TabelTennisPlayer (const string & fn = "none", const string & ln = "none", bool ht = false);
        void Name() const; // display ones name
        bool HasTable() const {return hasTable;};
        void ResetTable (bool v) {hasTable = v;};
    };
    
    #endif // TABTENN0_H_INCLUDED
    
    // 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)
    {
    }
    
    void TableTennisPlayer::Name() const
    {
        std::cout << lastname << ", " << firstname;
    }
    

    Recall that the constructor uses the member initializer list syntax in Chapter 12

    Now, players demand a class that includes the point ratings they earned in tournaments. Derive a class from the TableTennisPlayer class:

    class RatedPlayer : public TableTennisPlayer
    {
    ...
    };
    

    This is termed public derivation: the public members of the base class become public members of the derived class, the private portions of a base class become part of the derived class, but could only be accessed through public and protected methods of the base class
    Properties:
    1 an object of the derived class has stored within it the data members of the base type(inherits implementation)
    2 an object of the derived type could use the methods of the base type(inherits interface)
    What needs to be added:
    1 a derived class needs to have its own constructors
    2 a derived class can add additional data members and member functions as needed

    While constructing a derived object, the base-class object is constructed first, thus the derived class constructor should use member initializer list to pass base-class information to a base-class constructor first, then the derived-class constructor should initialize the data members that were added to the derived class.

    RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht) : TableTennisPlayer(fn, ln, ht)
    {
        rating = r;
    }
    

    If you don't do with a member initializer list, the default constructor of the base-class would be called
    Destroying an object do the opposite: the derived-class destructor is called first, and the base-class destructor is called automatically
    It is recommended to place the base class and the derive class's methods in one file, also one header file
    Example of using derived class:

    // tabtenn1.h -- a table-tennis base class
    #ifndef TABTENN1_H_INCLUDED
    #define TABTENN1_H_INCLUDED
    #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;};
    };
    
    class RatedPlayer : public TableTennisPlayer
    {
    private:
        unsigned int rating;
    public:
        RatedPlayer (unsigned int r = 0, const string & fn = "none", const string & ln = "none", bool ht = false);
        RatedPlayer (unsigned int r, const TableTennisPlayer & tp);
        unsigned int Rating() const {return rating;};
        void ResetRating (unsigned int r) {rating = r;};
    };
    
    #endif // TABTENN1_H_INCLUDED
    
    // tabtenn1.cpp -- simple base-class methods
    #include "tabtenn1.h"
    #include <iostream>
    
    TableTennisPlayer::TableTennisPlayer (const string & fn, const string & ln, bool ht) : firstname(fn), lastname(ln), hasTable(ht)
    {
    }
    
    void TableTennisPlayer::Name() const
    {
        std::cout << lastname << ", " << firstname;
    }
    
    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 TableTennisPlayer & tp) : TableTennisPlayer(tp), rating(r)
    {
    }
    
    // usett1.cpp -- using base class and derived class
    #include <iostream>
    #include "tabtenn1.h"
    
    int main(void)
    {
        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.
    ";
        else
            cout << ": hasn't a table.
    ";
        player1.Name();
        if (player1.HasTable())
            cout << ": has a table.
    ";
        else
            cout << ": hasn't a table.
    ";
        cout << "Name: ";
        rplayer1.Name();
        cout << "; Rating: " << rplayer1.Rating() << endl;
        RatedPlayer rplayer2(1212, player1);
        cout << "Name: ";
        rplayer2.Name();
        cout << "; Rating: " << rplayer2.Rating() << endl;
        return 0;
    }
    

    A derived class object could use base class methods provided that they are not private. A base class pointer and reference could also point/refer to a derived class object without type cast:

    RatedPlayer rplayer1;
    TableTennisPlayer * pt = RatedPlayer;
    pr->Name();
    TableTennisPlayer & rt = RatedPlayer;
    rt.Name();
    

    However, a base class reference/pointer could only invoke base-class methods. Also noteworthy that you can't assign base-class objects to derived-class pointers/references.
    One consequence is that arguments of base-class pointers/references could accept both base-class objects and derived-class objects
    You could also initialize a base-class object to a derived-class object:

    RatedPlayer olaf1(1840, "Olaf", "Loaf", true);
    TableTennisPlayer olaf2(olaf1);
    

    This initialization invokes the base-class copy constructor, which requires a reference to base-class objects, so the derived-class object would fit the requirements. So does assignment.


    13.2 Inheritance: An is-a relationship

    ) C++ has three varieties of inheritance: public, protected and private
    Public inheritance models a is-a relationship.
    It doesn't model a has-a relationship, a is-like-a relationship, a is-implemented-as-a relationship, a uses-a relationship.


    13.3 Polymorphic public inheritance

    ) Polymorphic: You can have multiple behaviors for a method depending on the object that invokes it.
    Key mechanism:
    1 redefining base-class methods in derived class
    2 using virtual methods

    Suppose you are going to write a Brass account for basic bank information, and a BrassPlus class to allow overdraft:

    // brass.h -- bank account classes
    #ifndef BRASS_H_INCLUDED
    #define BRASS_H_INCLUDED
    #include <string>
    
    class Brass
    {
    private:
        std::string fullName;
        long acctNum;
        double balance;
    public:
        Brass(const std::string & s = "Nullbody", long an = -1; double bal = 0.0);
        void Deposit(double amt);
        virtual void Withdraw(double amt);
        double Balance() const;
        virtual void ViewAcct() const;
        virtual ~Brass(){}
    };
    
    class BrassPlus : Brass
    {
    private:
        double maxLoan;
        double rate;
        double owesBank;
    public:
        BrassPlus(const std::string & s = "Nullbody", long an = -1, double bal = 0.0, double ml = 500, double r = 0.11125);
        BrassPlus(const Brass & ba, double ml = 500, double r = 0.11125);
        virtual void ViewAcct() const;
        virtual void Withdraw(double amt);
        void ResetMax(double m) { maxLoan = m;};
        void ResetRate(double r) {rate = r;};
        void ResetOwes() { owesBank = 0;}
    };
    
    #endif // BRASS_H_INCLUDED
    

    Noteworthy, both the Brass class and the BrassPlus class declared methods ViewAcct() and Withdraw(), and prefix it with keyword virtual, the two methods are termed virtual methods now:
    1 both classes defined ViewAcct(), but they are different methods, one Brass::ViewAcct() and one BrassPlus::ViewAcct()
    2 the feature of virtual: For methods defined in both base class and derived class, while called with pointers, if you don't use the keyword virtual, the program chooses a method according to the pointer or reference type. If you use the keyword virtual, the program chooses a method according to the object that a pointer of reference points to:

    // none virtual
    Brass dom;
    BrassPlus domPlus;
    Brass * p1 = dom;
    Brass * p2 = domPlus;
    p1->ViewAcct(); // use Brass::ViewAcct()
    p2->ViewAcct(); // use Brass::ViewAcct(), which is obviously not we wanted
    
    // virtual methods
    ...
    p2->ViewAcct(); // use BrassPlus::ViewAcct();
    

    Therefore, it's common practice to declare methods defined in both classes as virtual methods.
    3 The Brass class defined a virtual destructor, in order to make sure that the sequence of destructor calling is correct, which is usual practice

    The keyword virtual is only used in the method prototype in class declaration, not in method definitions:

    // brass.cpp -- bank account class methods
    #include <iostream>
    #include "brass.h"
    using std::cout;
    using std::endl;
    using std::string;
    
    typedef std::ios_base::fmtflags format;
    typedef std::streamsize precis;
    format setFormat();
    void restore(format f, precis p);
    
    Brass::Brass(const string & s, long an, double bal)
    {
        fullName = s;
        acctNum = an;
        balance = bal;
    }
    
    void Brass::Deposit(double amt)
    {
        if (amt < 0)
            cout << "Negative deposit not allowed; deposit is cancelled.
    ";
        else
            balance += amt;
    }
    
    void Brass::Withdraw(double amt)
    {
        format initialState = setFormat();
        precis prec = cout.precision(2);
        
        if (amt < 0)
            cout << "Withdrawal amount must be positive; withdrawal cancelled.
    ";
        else if (amt <= balance)
            balance -= amt;
        else
            cout << "Withdrawal amount of $" << amt << " exceeds your balance.
    " << "Withdrawal canceled.
    ";
        restore(initialState, prec);
    }
    
    double Brass::Balance() const
    {
        return balance;
    }
    
    void Brass::ViewAcct() const
    {
        format initialState = setFormat();
        precis prec = cout.precision(2);
        cout << "Client: " << fullName << endl;
        cout << "Account Number: " << acctNum << endl;
        cout << "Balance: $" << balance << endl;
        restore(initialState, prec);
    }
    
    BrassPlus::BrassPlus(const string & s, long an, double bal, double ml, double r) : Brass(s,an,bal)
    {
        maxLoan = ml;
        owesBank = 0.0;
        rate = r;
    }
    
    BrassPlus::BrassPlus(const Brass & ba, double ml, double r) : Brass(ba)
    {
        maxLoan = ml;
        owesBank = 0.0;
        rate = r;
    }
    
    void BrassPlus::ViewAcct() const
    {
        format initialState = setFormat();
        precis prec = cout.precision(2);
        Brass::ViewAcct();
        cout << "Maximum loan: $" << maxLoan << endl;
        cout << "Owed to bank: $" << owesBank << endl;
        cout.precision(3);
        cout << "Loan Rate: " << 100 * rate << "%
    ";
        restore(initialState, prec);
    }
    
    void BrassPlus::Withdraw(double amt)
    {
        format initialState = setFormat();
        precis prec = cout.precision(2);
        
        double bal = Balance();
        if (amt <= bal)
            Brass::Withdraw(amt);
        else if (amt <= bal + maxLoan - owesBank)
        {
            double advance = amt-bal;
            owesBank += advance * (1.0 + rate);
            cout << "Bank advance: $" << advance << endl;
            cout << "Finance charge: $" << advance * rate << endl;
            Deposit(advance);
            Bass::Withdraw(amt);
        }
        else
            cout << "Credit limit exceeded. Transaction cancelled.
    ";
        restore(initialState, prec);
    } 
    
    format setFormat()
    {
        return cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
    }
    
    void restore(format f, precis p)
    {
        cout.setf(f, std::ios_base::floatfield);
        cout.precision(p);
    }
    
  • 相关阅读:
    记录Integer比较问题
    代码中获取git输出
    python open mode
    elasticsearch Unrecognized VM option 'UseParNewGC'
    应用商店显示无法加载页面 请稍后重试
    deep learning with python前五章笔记
    QWeb2: Template 'systray_odoo_referral.gift_icon' not found
    wifi scapy
    struct.pack, struct.unpack详解
    python f-string
  • 原文地址:https://www.cnblogs.com/fsbblogs/p/9810818.html
Copyright © 2011-2022 走看看