类继承
C++提供了比修改代码更好的方法来拓展和修改类。 这种方法叫做类继承,它能够从已有的类派生出新的类,而派生类继承了原有类(称为基类)的特征,包括方法。 下面是可以通过类继承完成的一些工作。
- 可以在已有类的基础上添加功能。
- 可以给类添加数据。
- 可以修改类方法的行为。
- 一个简单的基类
从一个类派生出另一个类时,原始类称为基类,继承类称为派生类。
- 程序清单:
//tabtenn0.h -- 一个乒乓球基类
#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 // !TABTENN0_H_
//tabtenn0.cpp -- 简单基类方法
#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;
}
//usett0.cpp -- 使用基类
#include<iostream>
#include"tabtenn0.h"
int main(void)
{
using std::cout;
TableTennisPlayer player1("Chuck", "Blizzard", true);
TableTennisPlayer player2("Tara", "Boomdea", false);
player1.Name();
if (player1.HasTable())
cout << ": has a table.\n";
else
cout << ": hasn't a table.\n";
player2.Name();
if (player2.HasTable())
cout << ": has a table.\n";
else
cout << ": hasn't a table.\n";
system("pause");
return 0;
}
程序运行结果:
Blizzard, Chuck: has a table.
Boomdea, Tara: hasn't a table.
- 派生一个类
Webtown俱乐部的一些成员曾经参加过当地的乒乓球锦标赛,需要这样一个类,它能包括成员在比赛中的比分。 与其从零开始,不如从TableTennisClass类派生出一个类。 首先将RatedPlayer类声明为从TableTennisClass类派生而来:
//从TableTennisPlayer基类派生出的RatedPlayer类
class RatedPlayer: public TableTennisPlayer{
…
};
冒号指出RatedPlayer类的基类是TableTennisPlayer类。 使用公有派生,基类的公有成员将成为派生类的公有成员;基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问。
RatedPlayer对象将具有以下特征:
派生类对象存储了基类的数据成员(派生类继承了基类的实现);
派生类对象可以使用基类的方法(派生类继承了基类的接口)
因此,RatedPlayer对象可以存储运动员的姓名及其是否有球桌。 另外,RatedPlayer对象还可以使用TableTennisPlayer类的Name(),hasTable()和ResetTable()方法。
需要在继承特性中添加什么呢?
派生类需要自己的构造函数;
派生类可以根据需要添加额外的数据成员和成员函数。
在这个例子中,派生类需要另一个数据成员来存储比分,还应包含检索比分的方法和重置比分的方法。 因此,类声明与下面类似:
//简单的派生类
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; } //添加一个方法
};
构造函数必须给新成员和继承的成员提供数据。 在第一个RatedPlayer构造函数中,每个成员对应一个形参;而第二个RatedPlayer构造函数使用一个TableTennisPlayer参数,该参数包括firstname,lastname和hasTable。
- 构造函数:访问权限的考虑
派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问。 具体地说,派生类构造函数必须使用基类构造函数。
创建派生类对象时,程序首先创建基类对象。 从概念上说,这意味着基类对象应当在程序进入派生类构造函数之前被创建。 C++使用成员初始化列表语法来完成这种工作。
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 string & fn,
const string & ln,bool ht)//如果没有初始化列表会怎样
{
rating = r;
}
必须首先创建基类对象,如果不调用基类构造函数,程序将使用默认的基类构造函数,因此上述代码与下面等效:
RatedPlayer::RatedPlayer(unsigned int r, const string & fn,
const string & ln,bool ht)//:TableTennisPlayer()
{
rating = r;
}
除非要使用默认构造函数,否则应显式调用正确的基类构造函数。
下面来看第二个构造函数的代码:
RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp)
:TableTennisPlayer(tp)
{
rating = r;
}
这里也将TableTennisPlayer的信息传递给了TableTennisPlayer构造函数:
TableTennisPlayer(tp)
由于tp的类型为TableTennisPlayer&,因此将调用基类的复制构造函数。
有关派生类构造函数的要点如下:
首先创建基类对象;
派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;
派生类构造函数应初始化派生类新增的数据成员。
注意:创建派生类时,程序首先调用基类构造函数,然后再调用派生类构造函数。 基类构造函数负责初始化继承的数据成员;派生类构造函数主要用于初始化新增的数据成员。 派生类的构造函数总是调用一个基类构造函数。 可以使用初始化器列表语法指明要使用的基类构造函数,否则将使用默认的基类构造函数。
派生类对象过期时,程序将首先调用派生类析构函数,然后再调用基类析构函数。
- 使用派生类
//tabtenn1.h -- 一个乒乓球基类
#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; }
};
//简单的派生类
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 // !TABTENN0_H_
//tabtenn1.cpp -- 简单基类方法
#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::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 -- 使用基类和派生类
#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\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;
//用TableTennisPlayer对象初始化RatedPlayer
RatedPlayer rplayer2(1212, player1);
cout << "Name: ";
rplayer2.Name();
cout << "; Rating: " << rplayer2.Rating() << endl;
return 0;
}
程序运行结果:
Duck, Mallory: has a table
Boomdea, Tara: hasn't a table.
Name: Duck, Mallory; Rating: 1140
Name: Boomdea, Tara; Rating: 1212
- 派生类和基类之间的特殊关系
其中之一是派生类对象可以使用基类的方法,条件是方法不是私有的:
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
rplayer1.Name(); //派生对象使用基类方法
另外两个重要关系是:基类指针可以在不进行显式类型转换的情况下指向派生类对象;基类引用可以在不进行显式类型转换的情况下引用派生类对象:
RatedPlayer rplayer1(1140, "Mallory", "Duck", true);
TableTennisPlayer & rt = rplayer1;
TableTennisPlayer *pt = &rplayer1;
rt.Name(); //利用引用调用Name()
pt->Name(); //利用指针调用Name()
然而,基类指针或引用只能用于调用基类方法,因此,不能使用rt或pt来调用派生类的ResetRanking方法。 同时,不可以将基类对象和地址赋给派生类引用和指针。
- 继承:is-a关系
C++有3种继承方式:公有继承、保护继承和私有继承。 公有继承是最常用的方式,它建立一种is-a关系,即派生类对象也是一个基类对象,可以对基类对象执行的任何操作,也可以对派生类对象执行。
公有继承不建立has-a关系。 例如,午餐可能包括水果,但通常午餐并不是水果。 所以,不能通过从Fruit类派生出Lunch类来在午餐种添加水果。 在午餐中加入水果的正确方法是将其作为一种has-a关系:午餐有水果。 最容易的建模方式是,将Fruit对象作为Lunch类的数据成员。
- 多态公有继承
Ratedplayer继承示例很简单。 派生类对象使用基类的方法,而未做任何修改。 然而,可能会遇到这样的情况,即希望同一个方法在派生类和基类中的行为是不同的。 换句话来说,方法的行为应取决于调用该方法的对象。 这种较复杂的行为称为多态-具有多种形态。 有两种重要的机制可用于实现多态公有继承:
在派生类中重新定义基类的方法;
使用虚方法。
例如,下面是用于Brass Account支票账户的信息:
客户姓名;
账号;
当前结余。
下面是可以执行的操作:
创建账户;
存款;
取款;
显示账户信息。
Pontoon银行希望Brass Plus支票账户包含Brass Account的所有信息及如下信息:
透支上限;
透支贷款利率;
当前的透支总额。
不需要新增操作,但有两种操作的实现不同:
对于取款操作,必须考虑透支保护;
显示操作必须显示Brass Plus账户的其他信息。
假设第一个类命名为Brass,第二个类为BrassPlus。 应从Brass公有派生出BrassPlus。
- 开发Brass类和BrassPlus类
#include<string>
//Brass Account类
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();
};
//Brass Plus Account类
class BrassPlus :public 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_
说明:
- BrassPlus类在Brass类的基础上添加了3个私有数据成员和3个公有成员函数;
- Brass类和BrassPlus类都声明了ViewAcct()和Withdraw()方法,但BrassPlus对象和Brass对象的这些方法的行为是不同的;
- Brass类在声明ViewAcct()和Withdraw()时使用了关键字virtual。 这些方法称为虚方法(virtual method);
- Brass类还声明了一个虚析构函数,虽然该析构函数不执行任何操作。
第二点介绍了声明如何指出方法在派生类的行为的不同。 两个ViewAcct()原型表明将有2个独立的方法定义。 基类版本的限定名为Brass::ViewAcct(),派生类版本的限定名为BrassPlus::ViewAcct()。 程序将使用对象类型来确定使用哪个版本:
Brass dom(“Dominic Banker”,11224,4183.45);
BrassPlus dot(“Dorothy Banker”,12118,2592.00);
dom.ViewAcct(); //使用Brass::ViewAcct()
dot.ViewAcct(); //使用BrassPlus::ViewAcct()
同样,Withdraw()也有2个版本,一个供Brass对象使用,另一个供BrassPlus对象使用。 对于在两个类中行为相同的方法(如Deposit()和Balance()),则只在基类中声明。
第三点,如果没有使用关键字virtual,程序将根据引用类型或指针选择方法;如果使用了virtual,程序将根据引用或指针指向的对象的类型来选择方法。 如果ViewAcct()不是虚的,则程序的行为如下:
//非virtual ViewAcct()的行为
//根据引用类型确定方法
Brass dom("Dominic Banker", 11224, 4183.45);
BrassPlus dot("Dorothy Banker", 12118, 2592.00);
Brass & b1_ref = dom;
Brass & b2_ref = dot;
b1_ref.ViewAcct(); //使用Brass::ViewAcct()
b2_ref.ViewAcct(); //使用Brass::ViewAcct()
引用变量的类型为Brass,所以选择了Brass::ViewAccount()。 使用Brass指针代替引用时,行为将与此类似。
如果ViewAcct()是虚的。 则行为如下:
//virtual ViewAcct()的行为
//根据对象类型选择方法
Brass dom("Dominic Banker", 11224, 4183.45);
BrassPlus dot("Dorothy Banker", 12118, 2592.00);
Brass & b1_ref = dom;
Brass & b2_ref = dot;
b1_ref.ViewAcct(); //使用Brass::ViewAcct()
b2_ref.ViewAcct(); //使用BrassPlus::ViewAcct()
- 类实现:
//brass.cpp -- bank account类方法
#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::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 cancelled.\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::BrassPlus(const string &s, long an, double bal,
double m1, double r) :Brass(s, an, bal)
{
maxLoan = m1;
owesBank = 0.0;
rate = r;
}
BrassPlus::BrassPlus(const Brass & ba, double m1, double r)
:Brass(ba) //使用隐式复制构造函数
{
maxLoan = m1;
owesBank = 0.0;
rate = r;
}
//重定义ViewAcct()工作方式
void BrassPlus::ViewAcct() const
{
//建立##.##格式
format initialState = setFormat();
precis prec = cout.precision(2);
Brass::ViewAcct(); //显示基础部分
cout << "Maxium loan: $" << maxLoan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout.precision(3); //###.###格式
cout << "Loan Rate: " << 100 * rate << "%\n";
restore(initialState, prec); //恢复原始格式
}
//重定义Withdraw()工作方式
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);
Brass::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);
}
派生类构造函数在初始化基类私有数据时,采用的是成员初始化列表语法。
BrassPlus::BrassPlus(const string &s, long an, double bal,
double m1, double r) :Brass(s, an, bal)
{
maxLoan = m1;
owesBank = 0.0;
rate = r;
}
BrassPlus::BrassPlus(const Brass & ba, double m1, double r)
:Brass(ba) //使用隐式复制构造函数
{
maxLoan = m1;
owesBank = 0.0;
rate = r;
}
这几个构造函数都使用成员初始化列表语法,将基类信息传递给基类构造函数,然后使用构造函数体初始化BrassPlus类新增的数据项。
非构造函数不能使用成员初始化列表语法,但派生类方法可以调用公有的基类方法。 例如,BrassPlus版本的ViewAcct()核心内容如下:
void BrassPlus::ViewAcct() const
{
…
Brass::ViewAcct(); //显示基础部分
cout << "Maxium loan: $" << maxLoan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout.precision(3); //###.###格式
cout << "Loan Rate: " << 100 * rate << "%\n";
…
}
换句话说,BrassPlus::ViewAcct()显示新增的BrassPlus数据成员,并调用基类方法Brass::ViewAcct()来显示基类数据成员。
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);
Brass::Withdraw(amt);
}
else
cout << "Credit limit exceeded. Transaction cancelled.\n";
restore(initialState, prec);
}
接下来看BrassPlus::Withdraw()方法。 如果客户提取的金额超过了结余,该方法将安排贷款。 它可以使用Brass::Withdraw()来访问balance成员,但如果取款金额超过了结余,Brass::Withdraw将发出一个错误消息。 这种实现使用Deposit()方法进行放贷,然后在得到了足够的结余后调用Brass::Withdraw,从而避免了错误消息。
该方法使用基类的Balance()函数来确定结余。 因为派生类没有重新定义该方法,代码不必对Balance()使用作用域解析运算符。
- 使用Brass和BrassPlus类
//usebrass1.cpp -- 测试银行账户类
//和brass.cpp一起编译
#include<iostream>
#include"brass.h"
int main()
{
using std::cout;
using std::endl;
Brass Piggy("Porcelot Pigg", 381299, 4000.00);
BrassPlus Hoggy("Horatio Hogg", 382288, 3000.00);
Piggy.ViewAcct();
cout << endl;
Hoggy.ViewAcct();
cout << endl;
cout << "Depositing $1000 into the Hogg Account:\n";
Hoggy.Deposit(1000.00);
cout << "New balance: $" << Hoggy.Balance() << endl;
cout << "Withdrawing $4200 from the Pigg Account:\n";
Piggy.Withdraw(4200.00);
cout << "Pigg account balance: $" << Piggy.Balance() << endl;
cout << "Withdrawing $4200 from the Hogg Account:\n";
Hoggy.Withdraw(4200.00);
Hoggy.ViewAcct();
system("pause");
return 0;
}
程序运行结果:
Client: Porcelot Pigg
Account Number: 381299
Balance:$4000.00
Client: Horatio Hogg
Account Number: 382288
Balance:$3000.00
Maxium loan: $500.00
Owed to bank: $0.00
Loan Rate: 11.125%
Depositing $1000 into the Hogg Account:
New balance: $4000
Withdrawing $4200 from the Pigg Account:
Withdrawal amount of $4200.00 exceeds your balance.
WIthdrawal cancelled.
Pigg account balance: $4000
Withdrawing $4200 from the Hogg Account:
Bank advance: $200.00
Finance charge: $22.25
Client: Horatio Hogg
Account Number: 382288
Balance:$0.00
Maxium loan: $500.00
Owed to bank: $222.25
Loan Rate: 11.125%
- 演示虚方法的行为
假设要同时管理Brass和BrassPlus账户,如果能使用同一个数组来保存Brass和BrassPlus对象,将很有帮助,但这是不可能的。 数组中所以元素的类型必须相同,而Brass和BrassPlus是不同的类型。 然而,可以创建指向Brass的指针数组。 这样,每个元素的类型都相同,但由于使用的是公有继承模型,因此Brass指针既可以指向Brass对象,也可以指向BrassPlus对象。 因此,可以使用一个数组来表示多种类型的对象。 这就是多态性。
//usebrass2.cpp -- 多态例子
#include<iostream>
#include<string>
#include"brass.h"
const int CLIENTS = 4;
int main()
{
using std::cin;
using std::cout;
using std::endl;
Brass * p_clients[CLIENTS];
std::string temp;
long tempnum;
double tempbal;
char kind;
for (int i = 0; i < CLIENTS; i++)
{
cout << "Enter client's name: ";
getline(cin, temp);
cout << "Enter client's account number: ";
cin >> tempnum;
cout << "Enter opening balance: $";
cin >> tempbal;
cout << "Enter 1 for Brass Account or "
<< "2 for BrassPlus Account: ";
while (cin >> kind && (kind != '1'&&kind != '2'))
cout << "Enter either 1 or 2: ";
if (kind == '1')
p_clients[i] = new Brass(temp, tempnum, tempbal);
else
{
double tmax, trate;
cout << "Enter the overdraft limit: $";
cin >> tmax;
cout << "Enter the interest rate "
<< "as a decimal fraction: ";
cin >> trate;
p_clients[i] = new BrassPlus(temp, tempnum, tempbal, tmax, trate);
}
while (cin.get() != '\n')
continue;
}
cout << endl;
for (int i = 0; i < CLIENTS; i++)
{
p_clients[i]->ViewAcct();
cout << endl;
}
for (int i = 0; i < CLIENTS; i++)
{
delete p_clients[i]; //释放内存
}
cout << "Done.\n";
system("pause");
return 0;
}
程序运行结果:
Enter client's name: Harry Fishsong
Enter client's account number: 112233
Enter opening balance: $1500
Enter 1 for Brass Account or 2 for BrassPlus Account: 1
Enter client's name: Dinah Otternoe
Enter client's account number: 121213
Enter opening balance: $1800
Enter 1 for Brass Account or 2 for BrassPlus Account: 2
Enter the overdraft limit: $350
Enter the interest rate as a decimal fraction: 0.12
Enter client's name: Brenda Birdherd
Enter client's account number: 212118
Enter opening balance: $5200
Enter 1 for Brass Account or 2 for BrassPlus Account: 2
Enter the overdraft limit: $800
Enter the interest rate as a decimal fraction: 0.10
Enter client's name: Tim Turtletop
Enter client's account number: 233255
Enter opening balance: $688
Enter 1 for Brass Account or 2 for BrassPlus Account: 1
Client: Harry Fishsong
Account Number: 112233
Balance:$1500.00
Client: Dinah Otternoe
Account Number: 121213
Balance:$1800.00
Maxium loan: $350.00
Owed to bank: $0.00
Loan Rate: 12.000%
Client: Brenda Birdherd
Account Number: 212118
Balance:$5200.00
Maxium loan: $800.00
Owed to bank: $0.00
Loan Rate: 10.000%
Client: Tim Turtletop
Account Number: 233255
Balance:$688.00
Done.
多态性是由下述代码提供的:
for (int i = 0; i < CLIENTS; i++)
{
p_clients[i]->ViewAcct();
cout << endl;
}
如果数组指向的是Brass对象,则调用Brass::ViewAcct();如果指向的是BrassPlus对象,则调用BrassPlus::ViewAcct()。如果Brass::ViewAcct()被声明为虚的,则在任何情况下都将调用Brass::ViewAcct()。