多态公有继承

多态公有继承

基类的公有函数被派生类继承下来,但是派生类又重新定义了某个基类共有函数,也就是说同一个对象在派生类和基类的表现是不一样的,方法的的调用取决于调用方法的对象.

实现这种行为的方法:

  1. 在派生类中重新定义基类的方法

  2. 使用虚方法

下面设计一个类来演示这种方法

设计一个Brass类和BrassPlus类

需求如下:

支票账户如下:

  1. 客户姓名
  2. 账号
  3. 当前余额

执行操作:

  1. 创建账户
  2. 存款
  3. 取款
  4. 显示账户信息

银行可以透支,添加如下信息:

  1. 透支上限
  2. 透支利率
  3. 当前透支总额

改变以往操作:

  1. 取款考虑到透支上限
  2. 显示操作要显示新增信息

对于以上操作实现如下:

class Brass
	{
	private:
	    string fullName;		// 客户姓名
	    long acctNum;		// 账号
	    double balance;		// 余额
	public:
	    Brass(const string & s = "Nullbody", long an = -1,
		        double bal = 0.0);		// Brass默认构造函数
	    void Deposit(double amt);		// 存款
	    virtual void Withdraw(double amt);		// 虚方法取款
	    double Balance() const;		//  返回余额
	    virtual void ViewAcct() const;		// 虚方法打印类中的内容
	    virtual ~Brass() {}			// 后面说明虚析构函数的作用	
	};

	//Brass Plus是Brass的派生类
	class BrassPlus : public Brass
	{
	private:
	    double maxLoan;		// 透支上限
	    double rate;		// 透支利率
	    double owesBank;		// 欠帐
	public:
	    BrassPlus(const string & s = "Nullbody", long an = -1,
		    double bal = 0.0, double ml = 500,double r = 0.11125);		// BrassPlus默认构造函数
	    BrassPlus(const Brass & ba, double ml = 500,
		                        double r = 0.11125);		// BrassPlus构造函数
	    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; }
	};

在派生类中重新定义了方法ViewAcct(),Withdraw(double amt),所以可以直接通过调用的对象来确定使用的那个类中的方法.

但在这个例子中,重新定义的方法前面加入了virtual关键字,被称为虚方法,这个虚方法的作用如下:

  1. 如果方法是通过指针或者引用调用的,正常来讲,或者没有使用虚方法,就是通过指针类型或者引用类型来确定用什么方法.

  2. 如果使用了虚方法,那么就会根据指针和引用指向的对象来确定使用该对象的方法.

例:

// 假设ViewAcct()是虚方法
Brass dom("Dominic Banker" , 12118 , 4183.45);
BrassPlus dot("Dorothy Banker" , 12118 , 2592.00);
Brass & b1_ref = dom;
Brass & b2_ref = dot;
b1_ref.ViewAcct();		// 使用的是Brass的方法
b2_ref.ViewAcct();		// 使用的是BrassPlus的方法

若使用指针:

Brass * p1_brass = & dom;
Brass * p2_brass = & dot;
p1_brass->ViewAcct();		// 使用的是Brass的方法
p2_brass->ViewAcct();		// 使用的是BrassPlus的方法

Brass和BrassPlusd的实现:

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)		// 基类取钱
	{
	    if (amt < 0)
		cout << "Withdrawal amount must be positive; "

		     << "withdrawal 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		// 基类打印出类中数据
	{
	    cout << "Client: " << fullName << endl;
	    cout << "Account Number: " << acctNum << endl;
	    cout << "Balance: $" << balance << endl;
	}

	// BrassPlus方法
	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		// 重新定义ViewAcct()方法,增加打印派生类数据操作
	{
	    Brass::ViewAcct();   // 利用域运算符可以调用同名基类方法
	    cout << "Maximum loan: $" << maxLoan << endl;
	    cout << "Owed to bank: $" << owesBank << endl;
	    cout.precision(3);  
	    cout << "Loan Rate: " << 100 * rate << "%\n";
	}

	void BrassPlus::Withdraw(double amt)		// 重新定义Withdraw()方法,增加关于透支的相关操作
	{
	    double bal = Balance();		// 因为没有重新定义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";
	}

测试函数如下:

int main()
	{
	    using std::cout;
	    using std::endl;

	    Brass Piggy("Porcelot Pigg", 381299, 4000.00);		// 初始化基类对象
	    BrassPlus Hoggy("Horatio Hogg", 382288, 3000.00);		// 初始化派生类对象
	    
	    Piggy.ViewAcct();		// 调用基类的ViewAcct()方法
	    cout << endl;
	    Hoggy.ViewAcct();	// 调用派生类的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();
	    return 0;
	}

以上函数都是通过对象直接调用相应的接口函数,测试结果如下:

在这里插入图片描述

以上测试函数并没有体现出虚函数的作用,重新定义一个测试函数如下:

const int CLIENTS = 4;
	int main()
	{
	   using std::cin;
	   using std::cout;
	   using std::endl;

	   Brass * p_clients[CLIENTS];		// 基类指针数组
	   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);		// '1'是new出Brass对象
	       else															// '2'是new出BrassPlus对象
	       {
		   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();		// 指向Brass对象的调用Brass::ViewAcct(),指向BrassPlus对象的调用BrassPlus::ViewAcct()
	       cout << endl;
	   }
		      
	   for (int i = 0; i < CLIENTS; i++)
	   {
	       delete p_clients[i];  // 释放指针所指控间
	   }
	   cout << "Done.\n";
	   return 0; 
	}

效果如下:

在这里插入图片描述

为什么使用虚析构函数

delete释放某个对象的空间会调用相应指针的析构函数,就算Brass指针是指向BrassPlus对象,也会调用Brass的析构函数,如果是虚析构函数,则会调用指向对象的析构函数,然后自动调用基类的析构函数,这样才能保证正常的虚构函数被调用.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值