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

时间: 2024-10-10 02:50:44

Cpp Chapter 13: Class Inheritance Part1的相关文章

Cpp Chapter 12: Classes and Dynamic Memory Allocation Part1

12.1 Dynamic memory and classes 12.1.1 A review example and static class members Now try implement a String class(a flawed one): // strngbad.h -- flawed string class definition #include <iostream> #ifndef STRNGBAD_H_INCLUDED #define STRNGBAD_H_INCLU

《linux 内核完全剖析》chapter 13 内存管理 (不含swap.c)

内存管理(memory.c 和swap.s 部分) "倒着看" 先看memory management,很明显,前面各种阻力,都是因为涉及内存管理.不先看这个,我估计前面看了也是白看 我估算着理论打基础砸了差不多一个星期的时间在memory management上面了...感觉很有收获,是时候用实践(code)印证理论了! <modern operating system>讲内存管理那一章 http://blog.csdn.net/cinmyheart/article/de

零元学Expression Blend 4 - Chapter 13 用实例了解布局容器系列-「Pathlistbox」I

原文:零元学Expression Blend 4 - Chapter 13 用实例了解布局容器系列-「Pathlistbox」I 本系列将教大家以实做案例认识Blend 4 的布局容器,此章介绍的布局容器是Blend 4 里的-「Pathlistbox」 ? 本系列将教大家以实做案例认识Blend 4 的布局容器,此章介绍的布局容器是Blend 4 里的-「Pathlistbox」 ? 就是要让不会的新手都看的懂! ? <先来了解Pathlistbox的基本功能> 01 开启一个新专案後,在主

Cpp Chapter 9: Memory Models and Namespaces Part2

9.2.4 Static duration, external linkage ) External variables External variables are defined outside, thus external to any function.It is also termed global variables, which could be accessed in any function that follow's the external variable's defin

Cpp Chapter 10: Objects and Classes Part2

10.2.4 Using classes Following exapmle uses the class definition and implementation written in previous files: // usestok0.cpp -- the client program // compiler with stock00.cpp #include <iostream> #include "stock00.h" int main() { Stock s

Cpp Chapter 11: Working with Classes Part2

11.5.3 An implementation comment ) The separation of interface from implementation is one of the goals of OOP. ) Example of the implementation of the class Vector in a rand walk simulation: // randwalk.cpp -- using the Vector class // compile with th

Cpp Chapter 12: Classes and Dynamic Memory Allocation Part2

12.3 Things to remember when using new in constructors ) If you use new in constructors, use delete in destructor. Their use should be compatible, pair new with delete and new [] with delete [] ) Multiple constructors should share the same way of new

Cpp Chapter 9: Memory Models and Namespaces Part1

9.1 Separate compilation ) C++ could compile multiple files separately and link them into the final executable program ) You can divide original program into three parts(three files): 1. A header file that contains structure declarations and prototyp

Cpp Chapter 14: Reusing code in C++ Part1

14.1 Classes with object members ) The valarray class To declare an object, you follow the identifier valuearray with angle brackets that contain the desired type: valarray<int> q_values; valarray<double> weights; Several examples that use the