多态——具有多种形态。方法的行为应取决于调用该方法的对象。同一个方法的行为随上下文而异。
有两种重要的机制实现多态共有继承:
- 在派生类中重新定义基类的方法
- 使用虚方法
下面将具体实现:
类定义
//ss.h
#ifndef MYTIME_H_
#define MYTIME_H_
#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 : 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 ResstMax(double m) { maxLoan = m; }//重置最大贷款数目
void RessRate(double r) { rate = r; }//重置利率
void RessOwes() { owesBank = 0; }//重置欠账为0
};
#endif
基类:Brass 私有数据成员: fullName,acctNum, balance
派生类: BrassPlus 新增派生私有数据成员: maxLoan,rate,owesBank
函数的具体实现:
//file00.cpp
#include"ss.h"
#include <iostream>
using std::endl;
using std::cout;
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 positivive;"
<< "withdrawal canceled.\n";
else if (amt <= balance)
balance -= amt;
else
cout << "Withdrawal amount must of $" << amt
<< "excede your balance.\n"
<< "Withdrawal cancled.\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 Numer:" << 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;
rate = r;
owesBank = 0.0;
}
BrassPlus::BrassPlus(const Brass& ba, double ml, double r) : Brass(ba)
{
maxLoan = ml;
rate = r;
owesBank = 0.0;
}
void BrassPlus::ViewAcct() const
{
format initialState = setFormat();
precis prec = cout.precision(2);
Brass::ViewAcct();
cout << "Maximum loan: $" << maxLoan << endl;
cout << "Owed to back: $" << owesBank << endl;
cout.precision(3);
cout << "Lone 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);
Brass::Withdraw(amt);
}
else
cout << "Credit limit execeeded. 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 ml, double r) : Brass(s, an, bal)
{
maxLoan = ml;
rate = r;
owesBank = 0.0;
}
BrassPlus::BrassPlus(const Brass& ba, double ml, double r) : Brass(ba)
{
maxLoan = ml;
rate = r;
owesBank = 0.0;
}
这几个构造函数都是使用成员初始化列表语法,将基类信息传递给基类构造函数,然后使用构造函数体初始化类新增的数据项。
非构造函数不能使用成员初始化列表语法,但派生类方法可以调用基类方法。
同时注意代码使用作用域运算符来调用基类方法。
void BrassPlus::ViewAcct() const
{
...
ViewAcct();
....
}
如果没有使用作用域解析符,编译器就会认为ViewAcct()
是 BrassPlus::ViewAcct()
,这样创建了一个不会终止的递归函数。所以在实现时要加上::
仅使用Brass和BrassPlus类:
//file01.cpp
#include<iostream>
#include"ss.h"
int main()
{
using std::cout;
using std::endl;
Brass piggy("Porcelot Pigg", 381299, 4000.0);
BrassPlus Hoggy("Horatio Hogg", 382288, 300.0);
piggy.ViewAcct();
cout << endl;
Hoggy.ViewAcct();
cout << endl;
cout << "Despositing $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 << "Withdraw $4200 from the Hoggy Account:\n";
Hoggy.Withdraw(4200.00);
Hoggy.ViewAcct();
return 0;
}
使用虚方法的行为:
//file01.cpp
#include<iostream>
#include"ss.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 clint'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 thr 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";
return 0;
}
函数分析——虚析构函数
使用delete释放由new分配的对象代码说明为何基类应包含一个虚析构函数,虽然有时候用不到。如果虚析构函数不是虚的,那么将只能调用对应指针类型的析构函数。对于以上程序来说,使用delete时,只有Brass的析构函数被调用,即使指针指向的是BrassPlus类型的对象。析构函数是虚的,则将会调用相应对象类型的析构函数。虚构函数可以确保正确的析构函数被调用。
但是如果BrassPlus包含一个执行某些操作的析构函数,那么Brass必须有一个虚构函数,即使这个虚构函数不执行任何操作。