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

所属分类: 软件编程 / C 语言 阅读数: 107
收藏 0 赞 0 分享

一、前言

在上一篇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++基础——类继承中方法重载详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多