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 = -,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++基础——类继承中方法重载的更多相关文章
- python__基础 : 多继承中方法的调用顺序 __mro__方法
在多继承中,如果一个子类继承了两个平级的父类,而这两个父类有两个相同名字的方法,那么一般先继承谁,调用方法就调用先继承的那个父类的方法.如: class A: def test(self): prin ...
- C++ 类的继承五(类继承中的static关键字)
//类继承中的static关键字 #include<iostream> using namespace std; /* 派生类中的静态成员 基类定义的静态成员,将被所有派生类共享 根据静态 ...
- C# 类继承中的私有字段都去了哪里?
最近在看 C++ 类继承中的字段内存布局,我就很好奇 C# 中的继承链那些 private 字段都哪里去了? 在内存中是如何布局的,毕竟在子类中是无法访问的. 一:举例说明 为了方便讲述,先上一个例子 ...
- C++类继承中的构造函数和析构函数 调用顺序
思想: 在C++的类继承中,构造函数不能被继承(C11中可以被继承,但仅仅是写起来方便,不是真正的继承) 建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推: 析构对象时, ...
- (C++)C++类继承中的构造函数和析构函数
思想: 在C++的类继承中, 建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推: 析构对象时,其顺序正好与构造相反: 例子: #include <iostream& ...
- C++ 类的继承四(类继承中的重名成员)
//类继承中的重名成员 #include<iostream> using namespace std; /* 自己猜想: 对于子类中的与父类重名的成员,c++编译器会单独为子类的这个成员变 ...
- java基础疑难点总结之成员变量的继承,方法重载与重写的区别,多态与动态绑定
1.成员变量的继承 1.1要点 子类用extends关键字继承父类.子类中可以提供新的方法覆盖父类中的方法.子类中的方法不能直接访问父类中的私有域,子类可以用super关键字调用父类中的方法.在子类中 ...
- C# 类型运算符重载在类继承中的调用测试
这是一篇晦涩难懂的片面的研究 一,简单的继承层次 class CA { } class CB : CA{ } class CC : CB{ } } void Test(CA oa){//CATest ...
- C#面向对象(OOP)入门—第一天—多态和继承(方法重载)
面向对象是什么 面向对象是一种基于对象的编程方法,它取代了仅仅依靠方法和流程的编程方式.面向对象的编程语言中,对象(object)其实就是指特定类型.或某个类的实例.面向对象使得编程人员更容易组织和管 ...
随机推荐
- 如何通俗的理解spring的控制反转、依赖注入、面向切面编程等等
之前一直不理解spring的一些基础特性是什么意思,虽然网上的解释也很多,但是由于我比较笨,就是看不懂,知道最近才稍微了解,下面就以通俗讲解的方式记录下来. 前言 假设我是一个没有开店经验的小老板,准 ...
- Scrapy 1.4 文档 02 安装指南
安装 Scrapy Scrapy 运行在 Python 2.7 和 Python 3.3 或更高版本上. 如果您使用的是 Anaconda 或 Miniconda,则可以从 conda-forge 通 ...
- python函数注释, :与 ->
python函数注释, :与 -> 如图:add1函数中的:意思是:函数中的参数说明 add2函数中:->意思是:函数的返回值为整型 这两种方法都是函数的注释方法,具体使用时要别人能 ...
- box-shadow 详解及示例
box-shadow [bɑks] - [ˈʃædoʊ] 英文示意: box:盒,包厢 shadow:阴影,渐变 定义: box-shadow: none | <shadow> ...
- 你不知道的JavaScript--Item1 严格模式
本文转自[阮一峰博客]:http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html 一.概述 除了正常运行模式,ECMAscr ...
- Android 实现形态各异的双向侧滑菜单 自定义控件来袭
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39670935,本文出自:[张鸿洋的博客] 1.概述 关于自定义控件侧滑已经写了两 ...
- 提示“本地连接没有有效的ip配置-未修复“窗口
很多人在使用电脑时可能会遇到了这样一个网络问题,电脑无法连接网络,使用自带网络诊断工具诊断提示:"本地连接没有有效的ip配置".这种网络故障多数是出在使用路由器共享上网的windo ...
- 单机部署-consul
在本机开发环境中,直接通过以下命令可以启动一个单机consul服务器: ./consul agent -server -data-dir=/tmp/consul -bootstrap -adverti ...
- BZOJ_1345_[Baltic2007]序列问题Sequence_单调栈
BZOJ_1345_[Baltic2007]序列问题Sequence_单调栈 Description 对于一个给定的序列a1,…,an,我们对它进行一个操作reduce(i),该操作将数列中的元素ai ...
- CentOS7解决firefox无法启用ibus中文输入的问题
最近换电脑,要换掉使用了6年的旧环境,开始折腾重装系统: 下了minimal版本的CentOS7.4,然后开始一点点装想用的东西,多少找到一点十年前折腾LFS的感觉:然后竟然被输入法拌住了半天,事后回 ...