C++有3种继承方式:公有继承、保护继承、私有继承。公有继承是最常用的方式,它建立一种is-a关系,即派生类对象也是一个基类对象,可以对基类对象执行的任何操作,也可以对派生类对象执行。
如果希望同一个方法在派生类和基类中的行为是不同的,也就是说,方法的行为应取决于调用该方法的对象。这种较复杂的行为成为多态--具有多种形态,就是指同一个方法的想行为随上下文而异。有两种机制可用于实现多态公有继承:
1.在派生类中重新定义基类的方法。
2.使用虚方法。
为了说明上述性质,我们来看另一个例子:一个类用于 表示基本支票账户-Brass,另一个类用于表示代表Brass Plus支票账户,它添加了透支保护特性。也就是说,如果用户签出一张超出其存款余额的支票--但是超出的数额不是很大,银行将支付这张支票,对超出的部分收取额外的费用,并追加罚款。
下面是用于Brass支票账户的信息:
1.客户姓名
2.账户
3.当前余额
下面是可执行的操作:
1.创建账户
2.存款
3.取款
4.显示账户信息
而Brass Plus包含Brass的所有信息以及以下信息项:
1.透支上限
2.透支贷款利率
3.当前透支总额
不需要新增操作,担有两种操作的实现不同
1.对于取款操作,必须考虑透支保护
2.显示操作必须显示Brass Plus账户的其他信息
我们将第一个类命名为Brass,第二个类为BrassPlus。很明显,应从Prass公有派生出PrassPlus类。
程序清单 brass.h
//brass.h--bank account classes
#ifndef BRASS_H_
#define BRASS_H_
//Brass Account class
class Brass
{
private:
enum {MAX=35};
char fullName[MAX];
long acctNum;
double balance;
public:
Brass (const char *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
class BrassPlus: public Brass
{
private:
double maxloan;
double rate;
double owesBank;
public:
BrassPlus(const char *s = "Nullbody",long an=-1,
double bal=0.0,double ml=500,double r=0.10);
BrassPlus(const Brass & ba,double ml=500,double r=0.1);
virtual void ViewAcct()const;
virtual void Withdraw(double amt);
void ResetMax(double m){maxloan = m;}
void ReseRate(double r){rate = r;}
void ResetOwes(){owesBank = 0;}
};
#endif
对于上述程序清单,有以下说明:
BrassPlus类在Brass类的基础上添加了3个私有数据成员和3个公有成员函数。
Brass类和BrassPlus类都声明了ViewAcct()和Widthdraw()方法,但BrassPlus对象和Brass对象的这些方法的行为是不同的。
Brass类在声明ViewAcct()和Withdraw()时使用了新关键字virtual。这些方法被称为虚方法(virtual method)。
Brass还声明了一个虚拟析构函数,虽然该函数没进行任何操作。
记住:如果要在派生类中重新定义基类的方法,通常应将基类方法声明为虚拟的,这样,程序将根据对象类型而不是引用或指针的类型来选择方法版本;为基类声明一个虚拟析构函数也是一种惯例。
程序清单 brass.cpp
//brass.cpp--bank account class methods
#include <iostream>
#include <cstring>
#include "brass.h"
using std::cout;
using std::ios_base;
using std::endl;
//Brass methods
Brass::Brass(const char *s,long an,double bal)
{
std::strncpy(fullName,s,MAX - 1);
fullName[MAX - 1] - '\0';
acctNum = an;
balance = bal;
}
void Brass::Deposit(double amt)
{
if (amt < 0)
cout << "Neqative deposit not allowed: "
<< "deposit is cancelled.\n";
else
balance += amt;
}
void Brass::withdraw(double amt)
{
if (amt < 0)
cout << "Withdrawal amount must be positive: "
<< "withdrawall canceled.\n";
else if (amt <= balance)
balance -= amt;
else
cout << "Withdrawal amount of $" << amt
<< " exceeds your balance.\n"
<< "Withdrawal canceled.\n";
}
double Brass::Balance()const
{
return balance;
}
void Brass::ViewAcct()const
{
//set up ####.## format
ios_base::fmtflags initialState =
cout.setf(ios_base::fixed,ios_base::floatfield);
cout.setf(ios_base::showpoint);
cout.precision(2);
cout << "Client: " << fullName << endl;
cout << "Account Number: " << acctNum << endl;
cout << "Balance: $" << balance << endl;
cout.setf(initialState);//restore original format
}
//BrassPlus Methods
BrassPlus::BrassPlus(const char *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;
}
//redefine how viewacct()works
void BrassPlus::ViewAcct()const
{
//set up ###.## format
ios_base::fmtflags initalState =
cout.setf(ios_base::fixed,ios_base::floatfield);
cout.setf(ios_base::showpoint);
cout.precision(2);
Brass::ViewAcct();//display base portion
cout << "Maximum loan: $" << maxloan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout << "Loan Rate: " << 100 * rate <<
"%\n";
cout.setf(initalState);
}
//redefine how Withdraw()works
void BrassPlus::Withdraw(double amt)
{
//set up ###.## format
ios_base::fmtflags initalState =
cout.setf(ios_base::fixed,ios_base::floatfield);
cout.setf(ios_base::showpoint);
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";
cout.setf(initalState);
}
解析:
1.这里面的构造函数都使用成员初始化列表句法,将基类的信息传递给基类 的构造函数,然后使用构造函数初始化BrassPlus类新增的数据项。
2.非构造函数不能采用成员初始化列表句法,但派生类方法可以调用公有的基类方法。
记住:派生类并不能直接访问基类的私有数据,而必须使用基类的公有方法才能访问这些数据,访问的方式取决于方法。
程序清单 usebrass1.cpp
//usebrass1.cpp--testing bank account classes
//compile with brass.cpp
#include <iostream>
#include "brass.h"
int main(int argc, char * argv [ ])
{
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 Accout: \n";
Piggy.withdraw(4200.00);
cout << "Pigg accout balance $" << Piggy.Balance() << endl;
cout << "Withdrawing $4200 from the Hogg Account: \n";
Hoggy.Withdraw(4200.00);
Hoggy.ViewAcct();
return 0;
}
注意:如果在派生类中重新定义了基类的方法,则将它设置为虚方法,否则,设置为非虚方法。