基础知识

继承机制定义了父子(parent/child)关系。父类(parent)定义了所有子类(children)共通的共有接口(public interface)和私有实现(private implementation)。每个子类都可以增加或覆盖(override)继承而来的东西,以实现其自身独特的行为。在C++中,父类被称为基类(base class),子类被称为(derive class)。父类和子类之间的关系则称为继承体系(inheritance hierarchy)。

多态:让基类的pointer或reference得以十分透明地(transparently)指向其任何一个派生类的对象。在程序执行之前就已解析出应该调用哪一个函数,这种方式被称为静态绑定(static binding);但在面向对象编程方法中,编译器无法得知具体哪一份函数会被调用,这一解析操作会延迟至运行时(run-time)才进行,这就是所谓的动态绑定(dynamic binding)。

定义抽象类第一个步骤就是找出所有子类共通的操作行为,然后便是设法找出哪些操作行为与类型相关(type-dependent),也就是说有哪些操作行为必须根据不同的派生类而有不同的实现方式,这些操作行为应该成为整个继承体系中的虚函数(virtual function)。

设计抽象基类时,我们需要找出每一个操作行为的访问层级(access level)。如果某个操作行为应该让一般程序皆能访问,我们应该将它声明为public;但如果某个操作行为在基类之外不需要被用到,我们就将它声明为private,即使是该基类的派生类,亦无法访问基类中的private member;一个访问层级就是protected,这种层级行为可让派生类访问,不允许一般程序使用。

每个虚函数,要么得有其定义,要么可设为“纯”虚函数(pure virtual function),如果对于该类而言,这个虚函数并无实质意义的话,将虚函数赋值为0,意思便是另它为一个纯虚函数。任何类如果声明有一个(或多个)纯虚函数,那么,由于其接口的不完整性(纯虚函数没有函数定义,是谓不完整),程序无法为它产生任何对象,这种类只能作为派生类的子对象(subobject)使用,而且前提是这些派生类必须为所有虚函数提供确切的定义。另外根据一般规则,凡基类定义有一个(或多个)虚函数,应该要将其destructor声明为virtual。

派生类由两部分组成:一是基类构成的子对象,由基类的non-static data member——如果有的话——组成;二是派生类的部分(由派生类的non

-static data member组成)。类进行继承声明之前,其基类的定义必须已经存在。

data member如果是个reference,必须在constructor的member initialization list中加以初始化。一旦初始化,就再也无法指向另一个对象。如果data member是个pointer,就无此限制:我们可以在constructor内加以初始化,也可以先将它初始化为null,稍后再另它指向某个有效的内存地址。程序设计过程中我们便是根据这些不同的性质来决定要使用reference或pointer。

当我们定义派生类时,我们必须决定,究竟要将基类中的虚函数覆盖掉,还是原封不动地加以继承,如果我们继承了纯虚函数(pure virtual function),那么这个派生类也会被视为抽象类,也就无法为它定义任何对象。如果我们决定覆盖基类所提供的虚函数,那么派生类提供的新定义,其函数运行必须完全符合基类所声明的函数原型,包括:参数列表、返回类型、常量性(const-ness)。而且进行声明操作时,不一定得加上关键字virtual,编译器会依据两个函数的原型声明,决定某个函数是否会覆盖其基类中的同名函数。

练习题答案

练习5.1 实现一个两层的stack(堆栈)类体系。其基类是个纯抽象类Stack,只提供最简单的接口:pop()、push()、size()、empty()、full()、peek()和print()。两个派生类则为LIFO_Stack和Peekback_Stack。Peekback_Stack()可以让用户在不更改stack元素的前提下,访问任何一个元素。

#include <iostream>
#include <string>
#include <vector> using namespace std; typedef string elemType; class Stack
{
public:
virtual ~Stack(){}
virtual bool pop(elemType&) = 0;
virtual bool push(const elemType&) = 0;
virtual bool peek(int index, elemType&) = 0;
virtual int top() const = 0;
virtual int size() const = 0;
virtual bool empty() const = 0;
virtual bool full() const = 0;
virtual void print(ostream& = cout) const = 0;
}; ostream& operator<<(ostream& os, const Stack& rhs)
{
rhs.print();
return os;
} class LIFO_Stack :public Stack
{
public:
LIFO_Stack(int capacity = 0) :_top(0)
{
if (capacity)
_stack.reserve(capacity);
}
int size() const { return _stack.size(); }
bool empty()const { return !_top; }
bool full() const { return size() >= _stack.max_size(); }
int top() const { return _top; }
void print(ostream& os = cout) const;
bool pop(elemType& elem);
bool push(const elemType& elem);
bool peek(int, elemType&) { return false; } private:
vector<elemType> _stack;
int _top;
}; bool LIFO_Stack::pop(elemType& elem)
{
if (empty()) return false;
elem = _stack[--_top];
_stack.pop_back();
return true;
} bool LIFO_Stack::push(const elemType& elem)
{
if (full()) return false;
_stack.push_back(elem);
++_top;
return true;
} void LIFO_Stack::print(ostream& os) const
{
vector<elemType>::const_reverse_iterator rit = _stack.rbegin(),
rend = _stack.rend();
os << "\n\t";
while (rit != rend)
{
os << *rit++ << "\n\t";
}
os << endl;
} class Peekback_Stack :public Stack
{
public:
Peekback_Stack(int capacity = 0) :_top(0)
{
if (capacity)
_stack.reserve(capacity);
}
int size() const { return _stack.size(); }
bool empty()const { return !_top; }
bool full() const { return size() >= _stack.max_size(); }
int top() const { return _top; }
void print(ostream& os = cout) const;
bool pop(elemType& elem);
bool push(const elemType& elem);
bool peek(int, elemType&);
private:
vector<elemType> _stack;
int _top;
}; bool Peekback_Stack::pop(elemType& elem)
{
if (empty()) return false;
elem = _stack[--_top];
_stack.pop_back();
return true;
} bool Peekback_Stack::push(const elemType& elem)
{
if (full()) return false;
_stack.push_back(elem);
++_top;
return true;
} void Peekback_Stack::print(ostream& os) const
{
vector<elemType>::const_reverse_iterator rit = _stack.rbegin(),
rend = _stack.rend();
os << "\n\t";
while (rit != rend)
{
os << *rit++ << "\n\t";
}
os << endl;
} bool Peekback_Stack::peek(int index, elemType& elem)
{
if (empty())
return false;
if (index < 0 || index >= size())
return false;
elem = _stack[index];
return true;
} //non-member function peek()接受一个“抽象类Stack的reference”作为参数,
//并在函数内调用该Stack对象的虚函数peek()——此虚函数乃各派生类所特有。
void peek(Stack& st, int index)
{
cout << endl;
string t;
if (st.peek(index, t))
cout << "peek: " << t;
else
cout << "peek failed!";
cout << endl;
} int main()
{
LIFO_Stack st;
string str;
while (cin >> str && !st.full())
st.push(str);
cout << '\n' << "About to call peek() with LIFO_Stack" << endl;
peek(st, st.top() - 1);
cout << st; Peekback_Stack pst;
while (!st.empty())
{
string t;
if (st.pop(t))
pst.push(t);
}
cout << "About to call peek() with Peekback_Stack" << endl;
peek(pst, pst.top() - 1);
cout << pst; return 0;
}

end。

“博学慎思,明辨笃行。”

#《Essential C++》读书笔记# 第五章 面向对象编程风格的更多相关文章

  1. 《Linux内核设计与实现》第四周读书笔记——第五章

    <Linux内核设计与实现>第四周读书笔记--第五章 20135301张忻 估算学习时间:共1.5小时 读书:1.0 代码:0 作业:0 博客:0.5 实际学习时间:共2.0小时 读书:1 ...

  2. 《Linux内核设计与实现》读书笔记——第五章

    <Linux内核设计与实现>读书笔记--第五章 标签(空格分隔): 20135321余佳源 第五章 系统调用 操作系统中,内核提供了用户进程与内核进行交互的一组接口.这些接口让应用程序受限 ...

  3. 《Python 3.5从零开始学》笔记-第8章 面向对象编程

    前几章包括开启python之旅.列表和元组.字符串.字典.条件和循环等语句.函数等基本操作.主要对后面几章比较深入的内容记录笔记. 第8章 面向对象编程 8.3深入类 #!/usr/local/bin ...

  4. 《Linux内核设计与实现》读书笔记 第五章 系统调用

    第五章系统调用 系统调用是用户进程与内核进行交互的接口.为了保护系统稳定可靠,避免应用程序恣意忘形. 5.1与内核通信 系统调用在用户空间进程和硬件设备间添加了一个中间层, 作用:为用户空间提供了一种 ...

  5. Getting Started With Hazelcast 读书笔记(第五章,第六章)

    第五章 监听 本章应该是Hazelcast的核心机制了,Hazelcast通过注册各种监听器获悉集群中其他应用对数据的修改,成员的加入,退出等. 分为3个层次. 1.EntryListener(对数据 ...

  6. 《linux内核设计与实现》读书笔记第五章——系统调用

    第5章 系统调用 操作系统提供接口主要是为了保证系统稳定可靠,避免应用程序恣意妄行. 5.1 与内核通信 系统调用在用户空间进程和硬件设备之间添加了一个中间层. 该层主要作用有三个: 为用户空间提供了 ...

  7. 深入Java虚拟机读书笔记第五章Java虚拟机

    Java虚拟机 Java虚拟机之所以被称之为是虚拟的,就是因为它仅仅是由一个规范来定义的抽象计算机.因此,要运行某个Java程序,首先需要一个符合该规范的具体实现. Java虚拟机的生命周期 一个运行 ...

  8. C primer plus 读书笔记第五章

    本章的标题是运算符,表达式和语句.主要研究如何处理数据. 示例代码展示了一个使用简单的while循环的代码,难度不大. 下面简单介绍本章的基本概念. 1.基本运算符. 基本运算符有:赋值运算符(C语言 ...

  9. javascript 数据结构和算法读书笔记 > 第五章 队列

    队列是一种列表,但是它只能够在队尾插入元素,在队首删除元素.队列用于存储按照顺序排列的数据,先进先出.而栈则是后入栈的元素反而被优先处理. 实际中一般被应用在进程池.排队操作上面. 1. 队列的操作 ...

随机推荐

  1. React的环境搭建以及脚手架的安装

    1.安装node.js 如果安装了,就请参照第二步:没有的话,去node.js官网下载:https://nodejs.org/zh-cn/download/ 2.检查 win键+r -----> ...

  2. Cassandra2.2.10安装过程

    1. 2.安装JVM 3.OS环境配置: 关闭防火墙:service iptables stop vi /etc/sysctl.conf vm.zone_reclaim_mode=0 vm.max_m ...

  3. 小白学 Python 数据分析(2):Pandas (一)概述

    人生苦短,我用 Python 前文传送门: 小白学 Python 数据分析(1):数据分析基础 概览 首先还是几个官方链接放一下: Pandas 官网:https://pandas.pydata.or ...

  4. C++使用类调用CUDA核函数

    正如CUDA C所称,CUDA对C语言进行了很好的扩展,直接使用C语言可以非常简单方便的调用CUDA核函数.但是当想使用C++的类成员函数直接调用核函数是不可行的,第一,核函数不能作为类的成员函数,第 ...

  5. VS Code 1.42 发布!2020 年首个大更新

    近日(北京时间 2020 年 2 月 7 日),微软发布了 Visual Studio Code 1.42 版本,这也是 2020 年 VS Code 首次大更新.让我们来看看有哪些主要的更新. 支持 ...

  6. 实验12: OSPF

    实验9-1:单区域点到点链路的OSPF 实验目的通过本实验可以掌握:(1)在路由器上启动OSPF 路由进程(2)启用参与路由协议的接口,并且通告网络及所在的区域(3)度量值cost 的计算(4)点到点 ...

  7. Codeforces_714_A

    http://codeforces.com/problemset/problem/714/A 水,注意K的值. #include <iostream> using namespace st ...

  8. Python爬虫小结

    有些数据是没有专门的数据集的,为了找到神经网络训练的数据,自然而然的想到了用爬虫的方法开始采集数据.一开始采用了网上的一个动态爬虫的代码,发现爬取的图片大多是重复的,有效图片很少. 动态爬虫: fro ...

  9. SpringBoot使用ELK日志收集ELASTIC (ELK) STACK

    1:资源 # 文档向导 # logstash https://www.elastic.co/guide/en/logstash/current/index.html #kibana https://w ...

  10. Python应用——多变量的灵活处理

    本文始发于个人公众号:TechFlow,原创不易,求个关注 我们都知道Python是一个非常灵活的语言,以至于如果它不是你的第一门语言,你会发现它总能给你各种各样的惊喜,让你忍不住惊叹:woc,还有这 ...