一、前言

在上一篇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 = -,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 = -,
double bal = 0.0,double ml = ,double r = 0.11125);
BrassPlus(const Brass& ba,double ml = ,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 = ;}
}; #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 < )
cout << "Negative deposit not allowed;"
<< "deposit is cancelled.\n";
else
balance += amt;
} void Brass::Withdraw(double amt)
{
if(amt < )
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",,4000.00);
BrassPlus Hoggy("Horatio Hogg",,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",,4183.45);
BrassPlus dot("Dorothy Banker",,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 ;
}

usebrass.cpp

  打印结果:

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

C++基础——类继承中方法重载的更多相关文章

  1. python__基础 : 多继承中方法的调用顺序 __mro__方法

    在多继承中,如果一个子类继承了两个平级的父类,而这两个父类有两个相同名字的方法,那么一般先继承谁,调用方法就调用先继承的那个父类的方法.如: class A: def test(self): prin ...

  2. C++ 类的继承五(类继承中的static关键字)

    //类继承中的static关键字 #include<iostream> using namespace std; /* 派生类中的静态成员 基类定义的静态成员,将被所有派生类共享 根据静态 ...

  3. C# 类继承中的私有字段都去了哪里?

    最近在看 C++ 类继承中的字段内存布局,我就很好奇 C# 中的继承链那些 private 字段都哪里去了? 在内存中是如何布局的,毕竟在子类中是无法访问的. 一:举例说明 为了方便讲述,先上一个例子 ...

  4. C++类继承中的构造函数和析构函数 调用顺序

    思想: 在C++的类继承中,构造函数不能被继承(C11中可以被继承,但仅仅是写起来方便,不是真正的继承) 建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推: 析构对象时, ...

  5. (C++)C++类继承中的构造函数和析构函数

    思想: 在C++的类继承中, 建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推: 析构对象时,其顺序正好与构造相反: 例子: #include <iostream& ...

  6. C++ 类的继承四(类继承中的重名成员)

    //类继承中的重名成员 #include<iostream> using namespace std; /* 自己猜想: 对于子类中的与父类重名的成员,c++编译器会单独为子类的这个成员变 ...

  7. java基础疑难点总结之成员变量的继承,方法重载与重写的区别,多态与动态绑定

    1.成员变量的继承 1.1要点 子类用extends关键字继承父类.子类中可以提供新的方法覆盖父类中的方法.子类中的方法不能直接访问父类中的私有域,子类可以用super关键字调用父类中的方法.在子类中 ...

  8. C# 类型运算符重载在类继承中的调用测试

    这是一篇晦涩难懂的片面的研究 一,简单的继承层次 class CA { } class CB : CA{ } class CC : CB{ } } void Test(CA oa){//CATest ...

  9. C#面向对象(OOP)入门—第一天—多态和继承(方法重载)

    面向对象是什么 面向对象是一种基于对象的编程方法,它取代了仅仅依靠方法和流程的编程方式.面向对象的编程语言中,对象(object)其实就是指特定类型.或某个类的实例.面向对象使得编程人员更容易组织和管 ...

随机推荐

  1. Myeclipse10.7.1 导出war包报错

    myeclipse10.7.1 导出war问题解决办法myeclipse10破解后,导出war包时报"SECURITY ALERT: INTEGERITY CHECK ERROR" ...

  2. Oracle-08:连接查询

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 首先提供数据库脚本,供测试使用 create table DEPT ( deptno ) not null, ...

  3. windows10系统终极净化方法

    去年购入一台华硕FL8000U,性能很是不错,但是硬件只能兼容win10,不支持win7(linux倒是可以,但是始终用不顺手),win10里面杂七杂八的确实很多,本人重度强迫症+洁癖+极简主义,所以 ...

  4. Debian虚拟机安装VirtualBox增强功能

    作者:荒原之梦 原文链接:http://zhaokaifeng.com/?p=573 本文中使用的Debian是安装在VirtualBox中的虚拟机,具体参数如下: Debian版本:Linux de ...

  5. python教你用微信每天给女朋友说晚安

    但凡一件事,稍微有些重复.我就考虑怎么样用程序来实现它. 这里给各位程序员朋友分享如何每天给朋友定时微信发送"晚安",故事,新闻,等等··· ···最好运行在服务器上,这样后台挂起 ...

  6. poj~1236 Network of Schools 强连通入门题

    一些学校连接到计算机网络.这些学校之间已经达成了协议: 每所学校都有一份分发软件的学校名单("接收学校"). 请注意,如果B在学校A的分发名单中,则A不一定出现在学校B的名单中您需 ...

  7. TestNG失败自动截图

    转自:https://www.cnblogs.com/tobecrazy/p/4814813.html

  8. BZOJ_4773_负环_倍增弗洛伊德

    BZOJ_4773_负环 Description 在忘记考虑负环之后,黎瑟的算法又出错了.对于边带权的有向图 G = (V, E),请找出一个点数最小的环,使得 环上的边权和为负数.保证图中不包含重边 ...

  9. Spring事务管理----事物回滚

    Spring的事务管理默认只对未检查异常(java.lang.RuntimeException及其子类)进行回滚,如果一个方法抛出Checked异常,Spring事务管理默认不进行回滚. 改变默认方式 ...

  10. jsp 基础知识之指令元素

    由于考研和结业的事情,这里荒废了许久,而如今重新捡起来,是因为带到公司的碳素笔没有油了......    jsp的指令元素:通常以<%@开始,以%>结尾. jsp主要包括三种指令元素:pa ...