Cpp Chapter 13: Class Inheritance Part1
Posted fsbblogs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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
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.
";
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
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);
}
以上是关于Cpp Chapter 13: Class Inheritance Part1的主要内容,如果未能解决你的问题,请参考以下文章
Cpp Chapter 10: Objects and Classes Part2
Cpp Chapter 11: Working with Classes Part2