继承--is-a关系


    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.非构造函数不能采用成员初始化列表句法,但派生类方法可以调用公有的基类方法。
记住:派生类并不能直接访问基类的私有数据,而必须使用基类的公有方法才能访问这些数据,访问的方式取决于方法。


使用Brass和BrassPlus类
程序清单 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;
}

注意:如果在派生类中重新定义了基类的方法,则将它设置为虚方法,否则,设置为非虚方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

书灯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值