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

一、前言

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

(0)

相关推荐

  • C++中重载、重写(覆盖)和隐藏的区别实例分析

    本文实例讲述了C++中重载.重写(覆盖)和隐藏的区别,对于C++面向对象程序设计来说是非常重要的概念.具体分析如下: 1.重载:重载从overload翻译过来,是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型. 示例代码如下: class A{ public: void test(int i); void test(double i); void test(int i, double j); void te

  • c++类的隐式转换与强制转换重载详解

    在写这篇文章之前,让我们先回顾一下编译器通过匹配过程确定调用哪一个函数的匹配顺序:(1)寻找和使用最符合函数名和参数类型(包括返回值)的函数,若找到则调用: (2)否则,寻找一个函数模板,将其实例化产生一个匹配的重载函数,若找到则调用: (3)否则,寻找可以通过类型转换进行参数匹配的重载函数,若找到则调用它. 如果以上步骤均未找到匹配函数,则这个调用是错误的:如果这个调用有多于一个的匹配选译,则调用匹配出现二义性,也是错误的.   类型转换是将一种类型的值映射为另一种类型的值.类型转换实际上包含

  • 解析C++中不能重载为友元函数的四个运算符

    C++规定有四个运算符 =, ->, [], ()不可以是全局域中的重载(即不能重载为友员函数),这是为什么呢?现在先说说赋值运算符"="的重载C++规定赋值运算符"="只能重载为类的非静态成员函数,而不可以重载为类的友元函数.不能重载为类的静态成员应该比较容易理解,因为静态成员函数是属于整个类的,不是属于某个对象的,它只能去操作类静态数据成员.而赋值运算符"="是基于对象操作的.那么为什么赋值运算符不可以重载为类的友元函数?像同样都是双目

  • 详解C++中实现继承string类的MyString类的步骤

    昨天师兄又出了道测试题,让我们实现类似于string类的没有MyString类,刚开始很头疼,可是真正在自己写代码的时候又很兴奋的发现,这个过程真的是个很宝贵的机会,让我又有机会可以很好的熟悉回顾C++的很多知识-类设计,构造析构函数,成员函数,友元函数,引用,重载,字符串操作,动态内存分布.....于是昨天花了半天时间写了300多行代码,并认真的进行了相关测试.修改和总结.因为内容有点丰富,所以想分几次写出来,条理也清楚些. 类的空间分配:类给它的每个对象都分配了独立的空间去存储它的数据成员,

  • 深入解析C++中类的多重继承

    C++类的多继承 在前面的例子中,派生类都只有一个基类,称为单继承.除此之外,C++也支持多继承,即一个派生类可以有两个或多个基类. 多继承容易让代码逻辑复杂.思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java.C#.PHP 等干脆取消了多继承.想快速学习C++的读者可以不必细读. 多继承的语法也很简单,将多个基类用逗号隔开即可.例如已声明了类A.类B和类C,那么可以这样来声明派生类D: class D: public A, private B, protected C{ //类D新

  • C++中virtual继承的深入理解

    今天专门看了一下虚继承的东西,以前都没怎么用过,具体如下:父类:  复制代码 代码如下: class   CParent { .... }; 继承类的声明比较特别: class   CChild   :   virtual   public   CParent { .... } 请问,这个"virtual"是什么作用及含义? --------------------------------------------------------------- 表示虚拟继承,和普通继承是C++的

  • C++重载运算符的规则详解

    (1)C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载.例如,有人觉得BASIC中用"* *"作为幂运算符很方便,也想在C++中将"* *"定义为幂运算符,用"3* *5"表示35,这是不行的. (2)C++允许重载的运算符C++中绝大部分运算符都是可以被重载的. 不能重载的运算符只有5个: .             (成员访问运算符) .*            (成员指针访问运算符) ::             (域运

  • C++运算符重载的方法详细解析

    运算符重载实质上是函数的重载 重载运算符的函数一般格式如下: 函数类型    operator  运算符名称    (形参表列) {对运算符的重载处理} 例如,想将"+"用于Complex(复数)的加法运算,函数的原型可以是这样的: 复制代码 代码如下: Complex operator + (Complex & c1,Complex &c2); 其中,operator是关键字,时候专门用于定义重载运算符的函数的,运算符名称就是C++提供给用户的预定运算符. 注意:函数

  • C/C++ 公有继承、保护继承和私有继承的对比详解

    C/C++ 公有继承.保护继承和私有继承的区别 在c++的继承控制中,有三种不同的控制权限,分别是public.protected和private.定义派生类时,若不显示加上这三个关键字,就会使用默认的方式,用struct定义的类是默认public继承,class定义的类是默认private继承.这和Java有很大的不同,Java默认使用public继承,而且只有公有继承. 1.使用public继承时,派生类内部可以访问基类中public和protected成员,但是类外只能通过派生类的对象访问

  • C++中的三种继承public,protected,private详细解析

    三种访问权限 public:可以被任意实体访问 protected:只允许子类及本类的成员函数访问 private:只允许本类的成员函数访问 三种继承方式 public 继承 protect 继承 private 继承 组合结果 基类中 继承方式 子类中 public & public继承 => public public & protected继承 => protected public & private继承 = > private protected &am

随机推荐