基础知识

继承机制定义了父子(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. Python3基础之数据类型(字典)

    Python3数据类型之 字典 字典是另一种可变容器模型,且可存储任意类型对象. 字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({} ...

  2. linux--->redis php扩展安装

    阿里云centos6.9下 redis php扩展安装 下载phpredis wget http://pecl.php.net/get/redis-3.1.0.tgz 或 wget https://g ...

  3. 大数据之Kafka史上最详细原理总结

    Kafka Kafka是最初由Linkedin公司开发,是一个分布式.支持分区的(partition).多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实 ...

  4. 运行MapReduce任务

    1:运行MapReduce任务 1:原始数据准备 统计用户的登录次数: 源数据login_log.txt如下: 7whygb5m@linshiyouxiang.net nuahvuhuoia@qq.c ...

  5. Java虚拟机系列一:一文搞懂 JVM 架构和运行时数据区

    前言 之前写博客一直比较随性,主题也很随意,就是想到什么写什么,对什么感兴趣就写什么.虽然写起来无拘无束,自在随意,但也带来了一些问题,每次写完一篇后就要去纠结下一篇到底写什么,看来选择太多也不是好事 ...

  6. OpenResty学习指南(二)

    我的个人博客:https://www.luozhiyun.com/ 数据结构table table并没有区分开数组.哈希.集合等概念,而是揉在了一起. local color = {first = & ...

  7. vue中如何在本地导入js文件

    import {setStore,setUser,getStore,removeStore} from "../../../public/localstory" 在导入js文件时, ...

  8. sizeof('a')

    #include <iostream> using namespace std; int main(void) { cout << sizeof('a') << e ...

  9. 【Qt学习笔记】Qt+VS2010的配置

    http://blog.csdn.net/jocyln9026/article/details/8575218 关于Qt Qt是1991年由Trolltech公司开发的一个跨平台的C++图形用户界面应 ...

  10. Eclipse导入项目提示No projects are found to import解决办法

    使用Eclipse导入项目时遇到No projects are found to import提示的解决办法. 这是因为导入的文件里面缺少两个文件:.classpath.project 在这里三种方案 ...