详解C++基础——类继承中方法重载

 更新时间:2020年4月25日 17:25  点击:1539

一、前言

在上一篇C++基础博文中讨论了C++最基本的代码重用特性——类继承,派生类可以在继承基类元素的同时,添加新的成员和方法。但是没有考虑一种情况:派生类继承下来的方法的实现细节并不一定适合派生类的需求,此时派生类需要重载集成方法。

二、重载方法及虚函数

我们讨论《C++ Primer Plus》中的如下场景:银行记录客户信息,包括客户姓名、当前余额。客户这一类别当然能够创建客户对象、存款、取款以及显示信息。银行需要特殊记录具有透支权限的客户,因此这一类别的客户要额外记录透支上限、透支贷款利率以及当前透支总额。此外,取款和显示信息两个操作必须考虑客户的透支情况。综上,具有透支权限的客户是客户这一基类的派生类,派生类中不但需要添加新的成员,还要重载两个继承方法。

类声明代码:

#ifndef BRASS_H_
#define BRASS_H_

#include <string>

class Brass
{
private:
  std::string fullName;
  long acctNum;
  double balance;
public:
  Brass(const std::string& s = "Nullbody",long an = -1,double ba = 0.0);//default constructor
  void Deposit(double amt);
  double Balance() const;
  virtual void Withdraw(double amt);//virtual function
  virtual void ViewAcct() const;
  virtual ~Brass() {}//使用虚析构函数确保先调用继承类析构函数
};

//brass plus account class
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;}//inline function
  void ResetRate(double r) {rate = r;}
  void ResetOwes() {owesBank = 0;}
};

#endif

brass.h

类方法定义代码:

#include"brass.h"
#include <iostream>

using std::cout;
using std::endl;
using std::string;

//brass methods
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 methods
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;
}

//redefine viewacct()
void BrassPlus::ViewAcct() const
{
  Brass::ViewAcct();
  cout << "Maximum loan: $" << maxLoan << endl;
  cout << "Owed to bank: $" << owesBank << endl;
}

void BrassPlus::Withdraw(double amt)
{
  double bal = Balance();
  if(amt <= bal)
    Brass::Withdraw(amt);
  else if(amt <= bal + maxLoan - owesBank)// 已欠 + 此欠 ≤ maxLoan
  {
    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);// return to zero
  }
  else
    cout << "Credit limit exceeded. Transcation cancelled.\n" ;
}

brass.cpp

上述代码多了一个新的语法特性:虚函数(virtual function)。当基类声明中函数前加virtual,表示该函数为虚函数。区别在于当调用者是引用或者指针时,调用的是基类方法,还是派生类重载后的方法。具体区别我们后边在讨论。重中之重在于虚析构函数的意义。如果程序中使用delete删除占用的动态内存,且用于索引内存地址的指针类型是基类,那么即使该指针指向的是一个派生类对象,此时仅基类析构函数被调用。 我们着重观察brassPlus类重载的方法WithDraw有什么变化。这类客户由于具有透支权限,在取款时肯定要考虑欠款情况。若欲取出金额≤存储金额,则直接调用基类方法WithDraw,把存储金额减小;若欲取出金额大于存储金额,就必须进一步分析欠款情况。已欠款+此次欠款≤透支额度时,取款操作才有效。因此:owes+(amt - balance) ≤ maxLoan,进一步变形为:amt ≤ balance+maxLoan-owes。

三、应用程序示例及结果分析

现在看看应用程序代码和显示结果。APP代码:

#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 << endl;

  cout << "Withdrawing $4200 from the Pigg Account:\n";
  Piggy.Withdraw(4200.00);
  cout << "Pigg account balance: $" << Piggy.Balance() << endl;
  cout << endl;
  
  cout << "Withdrawing $4200 from the Hogg Account:\n";
  Hoggy.Withdraw(4200.00);
  Hoggy.ViewAcct();
  cout << endl;

  Brass dom("Dominic Banker",11224,4183.45);
  BrassPlus dot("Dorothy Banker",12118,2592.00);

  Brass& b1_ref = dom;
  Brass& b2_ref = dot;//use BrassPlus::ViewAcct() function

  b1_ref.ViewAcct();
  cout << endl;
  b2_ref.ViewAcct();
  cout << endl;

  return 0;
}

usebrass.cpp

打印结果:

Pigg和Hogg分别是基类和派生类对象。当两种均取款额度超出存储金额时,Hogg由于具有透支权限,才得以成功完成操作。注意之后创建的两个对象dom和dot,从调用ViewAcct()函数过程中再次体会虚函数的意义。若没有使用virtual关键字,程序根据引用或指针的类型选择使用基类方法还是派生类同名的重载后方法。若使用该关键字,则根据引用或指针所指向对象的类型来选择。程序中,b1_ref和b2_ref均是Brass类引用,但分别是Brass类对象dom和BrassPlus类对象dot的别名,因此使用virtual关键字后的ViewAcct()函数,依次调用基类和派生类方法。

以上所述是小编给大家介绍的C++基础——类继承中方法重载详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对猪先飞网站的支持!

[!--infotagslink--]

相关文章

  • C++ STL标准库std::vector的使用详解

    vector是表示可以改变大小的数组的序列容器,本文主要介绍了C++STL标准库std::vector的使用详解,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2022-03-06
  • C++中取余运算的实现

    这篇文章主要介绍了C++中取余运算的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 详解C++ string常用截取字符串方法

    这篇文章主要介绍了C++ string常用截取字符串方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • C++中四种加密算法之AES源代码

    本篇文章主要介绍了C++中四种加密算法之AES源代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...2020-04-25
  • C++ 整数拆分方法详解

    整数拆分,指把一个整数分解成若干个整数的和。本文重点给大家介绍C++ 整数拆分方法详解,非常不错,感兴趣的朋友一起学习吧...2020-04-25
  • 基于C#方法重载的总结详解

    本篇文章是对C#中方法重载进行了详细的分析介绍,需要的朋友参考下...2020-06-25
  • C++中 Sort函数详细解析

    这篇文章主要介绍了C++中Sort函数详细解析,sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变...2022-08-18
  • C++万能库头文件在vs中的安装步骤(图文)

    这篇文章主要介绍了C++万能库头文件在vs中的安装步骤(图文),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-23
  • 详解C++ bitset用法

    这篇文章主要介绍了C++ bitset用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • 浅谈C++中的string 类型占几个字节

    本篇文章小编并不是为大家讲解string类型的用法,而是讲解我个人比较好奇的问题,就是string 类型占几个字节...2020-04-25
  • C++ Eigen库计算矩阵特征值及特征向量

    这篇文章主要为大家详细介绍了C++ Eigen库计算矩阵特征值及特征向量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-04-25
  • C++ pair的用法实例详解

    这篇文章主要介绍了C++ pair的用法实例详解的相关资料,需要的朋友可以参考下...2020-04-25
  • VSCode C++多文件编译的简单使用方法

    这篇文章主要介绍了VSCode C++多文件编译的简单使用方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-29
  • C++中的循环引用

    虽然C++11引入了智能指针的,但是开发人员在与内存的斗争问题上并没有解放,如果我门实用不当仍然有内存泄漏问题,其中智能指针的循环引用缺陷是最大的问题。下面通过实例代码给大家介绍c++中的循环引用,一起看看吧...2020-04-25
  • C++随机点名生成器实例代码(老师们的福音!)

    这篇文章主要给大家介绍了关于C++随机点名生成器的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • C++如何删除map容器中指定值的元素详解

    map容器是C++ STL中的重要一员,删除map容器中value为指定元素的问题是我们经常与遇到的一个问题,下面这篇文章主要给大家介绍了关于利用C++如何删除map容器中指定值的元素的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。...2020-04-25
  • C++ 约瑟夫环问题案例详解

    这篇文章主要介绍了C++ 约瑟夫环问题案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下...2021-08-15
  • C++中cin的用法详细

    这篇文章主要介绍了C++中cin的用法详细,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-04-25
  • 基于C++中常见编译错误的总结详解

    本篇文章是对C++中的常见编译错误进行了详细的分析介绍,需要的朋友参考下...2020-04-25