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

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

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 
#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.\n";
    else
        cout << ": hasn't a table.\n";
    player1.Name();
    if (player1.HasTable())
        cout << ": has a table.\n";
    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;
}

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 

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 
#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.\n";
    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.\n";
    else if (amt <= balance)
        balance -= amt;
    else
        cout << "Withdrawal amount of $" << amt << " exceeds your balance.\n" << "Withdrawal canceled.\n";
    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 << "%\n";
    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.\n";
    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);
}

转载于:https://www.cnblogs.com/fsbblogs/p/9810818.html

你可能感兴趣的:(Cpp Chapter 13: Class Inheritance Part1)