一、前言

在上一篇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. Ruby类

    Ruby类 类定义 #!/usr/bin/ruby class Sample def hello puts "Hello Ruby!" end end # 使用上面的类来创建对象 ...

  2. MYSQL使用方法

    显示所有数据库:show databases; 创建数据库:create database 数据库名; 删除数据库:drop database 数据库名:   查看表结构: describe(desc ...

  3. Java开源生鲜电商平台-搜索模块的设计与架构(源码可下载)

    Java开源生鲜电商平台-搜索模块的设计与架构(源码可下载) 说明:搜索模块针对的是买家用户,在找菜品找的很费劲下的一种查询方面.目前也是快速的检索商品. 对于移动端的APP买家用户而言,要求的速度在 ...

  4. jquery-bootgrid

    http://www.jquery-bootgrid.com/GettingStarted 日志是生产环境非常重要的配置,在迁移老的工程到spring-boot时日志的设置兼容很重要,以下是自己在配置 ...

  5. ssh商城源码 2017.6.30

    http://www.cnblogs.com/chiangchou/p/project-ebuy.html http://www.java1234.com/vipzy.html源码视频链接

  6. 获取input Date日期 时间,并得到前一天的Date值

    var endTime = time.substring(0, 10); var temp1 = new Date(endTime.replace(/-/g,"/")); temp ...

  7. MongoDB中级---->关联多表查询

    http://www.linuxidc.com/Linux/2011-08/41043.htm DBRef is a more formal specification for creating re ...

  8. 关于linux find命令的使用

    find 和 xargs   xargs和find 在 使用find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行.但有些系统对能够传递给exec的命令 ...

  9. sql server 高可用镜像

    一.什么是数据库镜像 基本软件的高可用性解决方案 快速的故障转移恢复(3秒转移),低硬件成本 基于数据库级别的实现 二.数据库镜像中的服务器角色 主体服务器 承载主体数据库 接受用户连接和事务处理请求 ...

  10. bzoj1547 周末晚会

    我们要求方案数,还是旋转同构的,想burnside,如果我们能计算出转i位不变的满足条件的数量,那么这道题我们就解决了. 考虑转i位时,设tmp=gcd(i,n),那么就共有tmp个循环节. 当tmp ...