基础知识

继承机制定义了父子(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. Educational Codeforces Round 81 (Rated for Div. 2) B. Infinite Prefixes

    题目链接:http://codeforces.com/contest/1295/problem/B 题目:给定由0,1组成的字符串s,长度为n,定义t = sssssss.....一个无限长的字符串. ...

  2. Jmeter使用—使用 HTTP代理服务器抓取接口

    这里说一下怎么使用jmeter的HTTP代理服务器来抓取接口. 首先,打开jmeter,进入主页面,然后在对工作台(Jmeter版本4.0)点击右键->添加->非测试元件->HTTP ...

  3. zbar+opencv检测图片中的二维码或条形码

    zbar本身自带检测二维码条形码功能,这里使用opencv只是做一些简单的读取图片,灰度图片以及显示条形码和二维码时用到一些绘制 // barcode-qrcodescanner.cpp: 定义控制台 ...

  4. 使用dlib基于CNN(卷积神经网络)的人脸检测器来检测人脸

    基于机器学习CNN方法来检测人脸比之前介绍的效率要慢很多 需要先下载一个训练好的模型数据: 地址点击下载 // dlib_cnn_facedetect.cpp: 定义控制台应用程序的入口点. // # ...

  5. VLC for CentOS7

    https://blog.csdn.net/qiuyoujie/article/details/78486947 http://elearning.wsldp.com/pcmagazine/insta ...

  6. css的字体单位

    在css中的字体单位主要以px.em.rem为主.其中px也就是像素,是一种字体长度,它的长度是相对于显示器的品目分辨率而言的.一般情况下在浏览器中默认字体的大小是16px.其中em是相对字体.em的 ...

  7. asp获取隐藏域的json 并解析

    方法粗糙,适用度适中. var data2 = document.getElementById("hd_data02"); var val = data2.value; var o ...

  8. Oracle数据库安装与卸载

    一.下载俩个压缩包,同时选中解压到一个文件夹中 二.点击step.exe(win10可能弹出不满足环境要求,选择是就行了) 三.把接收更新勾掉不需要 四.选择创建和配置数据库 五.选择服务器类 六.选 ...

  9. Kubernetes学习(一)

    一 安装 三台机器搭建K8S集群,一台master两台woker Docker 18.09.0---kubeadm-1.14.0-0 kubelet-1.14.0-0 kubectl-1.14.0-0 ...

  10. StringBuffer StringBuilder String 区别

    String       字符串常量   不可变  使用字符串拼接时是不同的2个空间 StringBuffer  字符串变量   可变   线程安全  字符串拼接直接在字符串后追加 StringBui ...