基础知识

继承机制定义了父子(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. ios---设置UITabBarController的字体颜色和大小

    +(void)load{ NSMutableDictionary *attr3=[NSMutableDictionary dictionary]; attr3[NSForegroundColorAtt ...

  2. Shell常用语句及结构

    条件判断语句之if if 语句通过关系运算符判断表达式的真假来决定执行哪个分支:shell有三种if语句样式,如下: 语句1 if [ expression ] then Statement(s) t ...

  3. SpringCloud与微服务Ⅵ --- Ribbon负载均衡

    一.Ribbon是什么 Sping Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具. 简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户 ...

  4. docker 镜像位置切换

    发生现象: 在windows10下安装Docker for windows,随着用docker pull image文件后,C盘的容量越来越小了,你可能也有一种跟我一样的想法,想改变默认的安装路径,本 ...

  5. python学习Day04--列表

    [主要内容] 1.列表的索引: lst=[1,2,3,4,5,6] print(lst[0])      #获取第一个元素 lst[1]='你好'    #改变列表中的值 2.列表的切片: lst=[ ...

  6. 面试官:"谈谈分库分表吧?"

    转自:学习Java的小姐姐 www.cnblogs.com/chenchen0618/p/11624480.html 1.什么是分库分表 从字面上简单理解,就是将原本存储在一个库的数据分块存储在多个库 ...

  7. sublime: javascript/css 的格式化

    Sublime Text 3 破解版 + 注册机 + 汉化包 + 教程 http://www.xiumu.org/note/sublime-text-3.shtml 1.sublime 如果控制菜单选 ...

  8. Netty学习(2):IO模型之NIO初探

    NIO 概述 前面说到 BIO 有着创建线程多,阻塞 CPU 等问题,因此为解决 BIO 的问题,NIO 作为同步非阻塞 IO模型,随 JDK1.4 而出生了. 在前面我们反复说过4个概念:同步.异步 ...

  9. 幸存者偏差Survivorship Bias

    "最不符合逻辑的地方,一定埋藏着最深刻的逻辑."——余秋雨<行者无疆> 为什么要说幸存者偏差? 因为2018年全国II卷的描述即为典型的“幸存者偏差”,且这一例子被引入 ...

  10. 2019全国大学生信息安全大赛两道web

    简单小结 菜鸟第一次打国赛,这次题目质量很高,学到了许多姿势. Web Justsoso 打开题目,源代码出存在提示: 使用LFI读取index.php与hint.php http://d4dc224 ...