C++ 知识点记录(6)类的继承(Cap13)
1. 多态公有继承
派生类不能直接访问基类的私有数据,而必须使用基类的公有方法才能访问这些数据。访问的方式取决于方法。构造函数使用一种技术,而其他成员函数使用另一种技术。派生类构造函数在初始化基类私有数据时,采用的是成员初始化列表语法。非构造函数不能使用成员初始化列表语法,但派生类方法可以调用公有的基类方法。在派生类方法中,标准技术是使用作用域解析运算符来调用基类方法。
#pragma once
// brass.h -- bank account classes -- 13.7
#ifndef BRASS_H_
#define BRASS_H_
#include <string>// Brass Account 支票账户信息
class Brass {
private:std::string fullName; // 客户姓名long acctNum; // 账号double balance; // 当前结余
public:Brass(const std::string& s = "Nullbody", long an = -1, double bal = 0.0); // 构造函数,创建账户void Deposit(double amt); // 存款virtual void Withdraw(double amt); // 取款double Balance() const; // 显示余额virtual void ViewAcct() const; // 显示账户信息virtual ~Brass(){} // 析构函数
};// Brass Plus Account 信息类
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; // 显示账户信息,需显示Brass Plus账户的其他信息virtual void Withdraw(double amt); // 取款,需考虑透支保护void ResetMax(double m) { maxLoan = m; } // 重置透支限额void ResetRate(double r) { rate = r; } // 重置利率void ResetOwes() { owesBank = 0; } // 重置欠款总额
};#endif // !BRASS_H_
// brass.cpp -- bank account class methods
#include <iostream>
#include <string>
#include "brass.h"
using std::cin;
using std::cout;
using std::string;
using std::endl;// formatting stuff
typedef std::ios_base::fmtflags format;
typedef std::streamsize precis;
format setFormat();
void restore(format f, precis p);// 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";elsebalance += amt;
}void Brass::Withdraw(double amt) {// set up ###.## formatformat initialState = setFormat();precis prec = cout.precision(2);if (amt < 0)cout << "Withdrawal amount must be positive; withdrawal canceled.\n";else if (amt <= balance)balance -= amt;elsecout << "Withdrawal amount of $" << amt<< " exceeds your balance.\n"<< "Withdrawal canceled.\n";restore(initialState, prec);
}double Brass::Balance() const {return balance;
}void Brass::ViewAcct() const {// set up ###.## formatformat initialState = setFormat();precis prec = cout.precision(2);cout << "Client: " << fullName << endl;cout << "Account Number:" << acctNum << endl;cout << "Balance: $" << balance << endl;restore(initialState, prec); // restore original format
}// 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 how ViewAcct() works
void BrassPlus::ViewAcct() const {// set up ###.## formatformat initialState = setFormat();precis prec = cout.precision(2);Brass::ViewAcct();cout << "Maximum loan: $" << maxLoan << endl;cout << "Owed to bank: $" << owesBank << endl;cout.precision(3);cout << "Loan Rate: " << 100 * rate << "%\n";restore(initialState, prec);
}// redefine how Withdraw() works
void BrassPlus::Withdraw(double amt) {// set up ###.## formatformat initialState = setFormat();precis prec = cout.precision(2);double bal = Balance();if (amt <= bal)Brass::Withdraw(amt);else if (amt <= bal + maxLoan - owesBank) {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);}else {cout << "Credit limit exceeded. Transaction cancelled.\n";}restore(initialState, prec);
}format setFormat() {// set up ###.## formatreturn cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
}void restore(format f, precis p) {cout.setf(f, std::ios_base::floatfield);cout.precision(p);
}
程序清单13.9 使用Brass类和BrassPlus类。
// usebrass1.cpp -- 13.9
#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 << "Withdrawing &4200 from the Pigg Account:\n";Piggy.Withdraw(4200.00);cout << "Pigg account balance: $" << Piggy.Balance() << endl;cout << "Withdrawing $4200 from the Hogg Account:\n";Hoggy.Withdraw(4200.00);Hoggy.ViewAcct();return 0;
}
如果方法是通过引用或指针而不是对象调用的,它将确定使用哪一种方法。如果没有使用关键字virtual,程序将根据引用类型或指针类型选择方法;如果使用了virtual,程序将根据引用或指针指向的对象的类型来选择方法。
// usebrass2.cpp -- polymorphic example
#include <iostream>
#include <string>
#include "brass.h"
const int CLIENTS = 4;int main() {using std::cin;using std::cout;using std::endl;Brass* p_clients[CLIENTS];std::string temp;long tempnum;double tempbal;char kind;for (int i = 0; i < CLIENTS; i++) {cout << "Enter client's name: ";getline(cin, temp);cout << "Enter client's account number: ";cin >> tempnum;cout << "Enter opening balance: $";cin >> tempbal;cout << "Enter 1 for Brass Account or 2 for BrassPlus Account: ";while (cin >> kind && (kind != '1' && kind != '2')) {cout << "Enter either 1 or 2: ";}if (kind == '1')p_clients[i] = new Brass(temp, tempnum, tempbal);else {double tmax, trate;cout << "Enter the overdraft limit: $";cin >> tmax;cout << "Enter the interest rate as a decimal fraction: ";cin >> trate;p_clients[i] = new BrassPlus(temp, tempnum, tempbal, tmax, trate);}while (cin.get() != '\n')continue;}cout << endl;for (int i = 0; i < CLIENTS; i++) {p_clients[i]->ViewAcct();cout << endl;}for (int i = 0; i < CLIENTS; i++) {delete p_clients[i]; // free memory}cout << "Done.\n";return 0;
}
需要虚析构函数。
2. 静态联编和动态联编
程序调用函数时,将使用哪个可执行代码块?由于函数重载,编译器必须查看函数参数以及函数名才能确定使用哪个函数。C/C++编译器可以在编译过程完成这种联编,这被称为静态联编(static binding),又称早期联编。虚函数使这项工作变得更困难,编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编(dynamic binding),又称为晚期联编(late binding)。
纯虚函数、抽象基类
#pragma once
// acctabc.h
#ifndef ACCTABC_H_
#define ACCTABC_H_
#include <iostream>
#include <string>// Abstract Base Class
class AcctABC {
private:std::string fullName;long acctNum;double balance;
protected:struct Formatting {std::ios_base::fmtflags flag;std::streamsize pr;};const std::string& FullName() const { return fullName; }long AcctNum() const { return acctNum; }Formatting SetFormat() const;void Restore(Formatting& f) const;
public:AcctABC(const std::string& s = "Nullbody", long an = -1, double bal = 0.0);void Deposit(double amt);virtual void Withdraw(double amt) = 0; // 纯虚函数double Balance() const { return balance; }virtual void ViewAcct() const = 0;virtual ~AcctABC() {}
};// Brass Account Class
class Brass : public AcctABC {
public:Brass(const std::string& s = "Nullbody", long an = -1, double bal = 0.0) : AcctABC(s, an, bal) {}virtual void Withdraw(double amt);virtual void ViewAcct() const;virtual ~Brass() {}
};// Brass Plus Account Class
class BrassPlus : public AcctABC {
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.10);BrassPlus(const Brass& ba, double ml = 500, double r = 0.10);virtual void ViewAcct() const;virtual void Withdraw(double amt);void ResetMax(double m) { maxLoan = m; }void ResetRate(double r) { rate = r; }void ResetOwes() { owesBank = 0; }
};#endif // !ACCTABC_H_
// acctabc.cpp
#include <iostream>
#include "acctabc.h"
using std::cout;
using std::ios_base;
using std::endl;
using std::string;// Abstract Base Class
AcctABC::AcctABC(const string& s, long an, double bal) {fullName = s;acctNum = an;balance = bal;
}void AcctABC::Deposit(double amt) {if (amt < 0)cout << "Negative deposit not allowed; deposit is cancelled.\n";elsebalance += amt;
}void AcctABC::Withdraw(double amt) {balance -= amt;
}// 保护方法
AcctABC::Formatting AcctABC::SetFormat()const {// set up ###.## formatFormatting f;f.flag = cout.setf(ios_base::fixed, ios_base::floatfield);f.pr = cout.precision(2);return f;
}void AcctABC::Restore(Formatting& f) const {cout.setf(f.flag, ios_base::floatfield);cout.precision(f.pr);
}// Brass 方法
void Brass::Withdraw(double amt) {if (amt < 0)cout << "Withdrawal amount must be positive; withdrawal canceled.\n";else if (amt <= Balance())AcctABC::Withdraw(amt);elsecout << "Withdrawal amount of $" << amt << " exceeds your balance.\nWithdrawal canceled.\n";
}void Brass::ViewAcct() const {Formatting f = SetFormat();cout << "Brass Client: " << FullName() << endl;cout << "Account Number: " << AcctNum() << endl;cout << "Balance: $" << Balance() << endl;Restore(f);
}// BrassPlus Methods
BrassPlus::BrassPlus(const string& s, long an, double bal, double ml, double r) : AcctABC(s, an, bal) {maxLoan = ml;owesBank = 0.0;rate = r;
}BrassPlus::BrassPlus(const Brass& ba, double ml, double r) : AcctABC(ba) {maxLoan = ml;owesBank = 0.0;rate = r;
}void BrassPlus::ViewAcct() const {Formatting f = SetFormat();cout << "BrassPlus Client: " << FullName() << endl;cout << "Account Number: " << AcctNum() << endl;cout << "Balance: $" << Balance() << endl;cout << "Maximum loan: $" << maxLoan << endl;cout << "Owed to bank: $" << owesBank << endl;cout.precision(3);cout << "Loan Rate: " << 100 * rate << "%\n";Restore(f);
}void BrassPlus::Withdraw(double amt) {Formatting f = SetFormat();double bal = Balance();if (amt <= bal)AcctABC::Withdraw(amt);else if (amt <= bal + maxLoan - owesBank) {double advance = amt - bal;owesBank += advance * (1.0 + rate);cout << "Bank advance: $" << advance << endl;cout << "Finance charge: $" << advance * rate << endl;Deposit(advance);AcctABC::Withdraw(amt);}else {cout << "Credit limit exceeded. Transaction cancelled.\n";}
}
ABC理念:设计ABC之前,首先应开发一个模型——指出编程问题所需的类以及它们之间相互关系。可以将ABC看作是一种必须实施的接口。
3. 继承和动态内存分配
当基类和派生类都采用动态内存分配时,派生类的析构函数、复制构造函数、赋值运算符都必须使用相应的基类方法来处理基类元素。这种要求是通过三种不同的方式来满足的。对于析构函数,这是自动完成的;对于构造函数,这是通过在初始化成员列表中调用基类的复制构造函数来完成的;如果不这样做,将自动调用基类的默认构造函数。对于赋值运算符,这是通过使用作用域解析运算符显式地调用基类的赋值运算符来完成的。
4. 小结
(1)派生类从基类那里继承了什么?
基类的公有成员成为派生类的公有成员,基类的保护成员成为派生类的保护成员,基类的私有成员被继承,但不能直接访问。
(2)派生类不能从基类那里继承什么?
不能继承构造函数、析构函数、赋值运算符和友元。
(3)可以将派生类对象赋给基类对象吗?可以将基类对象赋给派生类对象吗?
可以将派生类对象赋给基类对象,对于派生类的新增数据成员都不会传递给基类对象。然而,程序将使用基类的赋值运算符。仅当派生类定义了转换运算符(即包含将基类引用作为唯一参数的构造函数)或使用基类为参数的赋值运算符时,相反方向的赋值才是可能的。
C++ 知识点记录(6)类的继承(Cap13)相关推荐
- C++知识点44——类的继承概述
类的继承,是新类从已有类那里得到已有的特性,或从已有类产生新类的过程.原有类称为基类或父类,新类称为派生类或子类. 子类通过类派生列表确定子类继承了哪些类.类派生列表的形式如下 class 子类名:访 ...
- c++基础知识点(6)类的继承,构造,析构顺序,虚继承等
学习继承之前,我们先回顾下protected(保护)成员 protected 成员的特点与作用: -对建立其所在类对象的模块来说,它与private成员的性质相同 -对于其派生类来说,它与public ...
- 类的继承与多态知识点总结
一.类的访问权限 class A {private:int x; public:int z; public:void test() {x = 1;//okz = 1;//okA a;a.x = 1;/ ...
- javaweb基础知识点记录1
javaweb基础知识点记录 1.当我们通过在浏览器的输入栏中直接输入网址的方式访问网页的时候,浏览器采用的就是GET方法向服务器获取资源. 2.我们可以将Servlet看做是嵌套了HTML代码的ja ...
- C++部分知识点记录
文章目录 反问 为什么想要加入国微芯? 自己的优势/岗位匹配度在哪? EDA(电子设计自动化) 前言 <C++ Primer>知识点记录 1.声明一个返回数组指针的函数(P205) 2.头 ...
- java类接口实验_实验3_Java类的继承和接口的定义和使用
本次有三题:学会Java类的继承.接口的定义和使用 // Ex3_1.java /** * 题目要求: * 修改例5.7(P95)实现在页面中拖动鼠标画出矩形,矩形的对角线为点击并拖动鼠标形成的直线线 ...
- python常用知识点总结-python 类知识点总结
python 类知识点总结 面向对象思想: 1.设计的时候,一定要明确应用场景 2.由对象分析定义类的时候,找不到共同特征和技能不用强求 1.简述类.对象.实例化.实例这些名词的含义: 类:从一组对象 ...
- 27、Python 面向对象(创建类、创建实例对象、访问属性、内置类属性、对象销毁、类的继承、方法重写、基础重载方法、运算符重载、类属性与方法、下划线双下划线)
27Python面向对象(Python2) Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的.本章节我们将详细介绍Python的面向对象编程. ...
- 关于python类的继承正确的说法是_2017美团点评的运维岗校招笔试题,测测你会几题?...
原标题:2017美团点评的运维岗校招笔试题,测测你会几题? 1.数据库:以下哪项不是HASH索引的特征? A MySQL不能确定在两个值之间大约有多少行 B 不能使用hash索引来加速ORDER BY ...
最新文章
- vb 搜索指定目录下的指定类型文件
- Ubuntu 11.10 开机让 Varnish 跟随 Nginx 一起启动
- 河南大学计算机专业导师,侯彦娥 - 河南大学 - 计算机与信息工程学院
- 余承东 鸿蒙不是手机,鸿蒙手机,来了!余承东:没有人会是一座孤岛
- Android 图形驱动初始化
- epoll示例程序——服务端
- 《Online Filtering Training Samples for Robust Visual Tracking》解析
- 原生JS实现简易转盘抽奖
- 写论文时introduction and realted works部分如何写别人的工作,论文写作常用词
- 网络流专题(最大流与费用流)例题总结
- MOS管在开关电路中的使用
- 软件系统开发费用的估算——功能点方法 应用实例
- laravel5.6 使用mail发送附件邮件
- 有关 ovirt 的分析
- 【搬运】常用逻辑符号整理
- TypeError: argument of type ‘NoneType‘ is not iterable
- 基于C#的服装店进销存管理系统设计与实现
- 为什么现在JAVA初级程序员要求这么高?
- 我为什么愿意帮助这样的人
- 新华网:南通市创新大数据管理应用取得显著成效