基础知识

继承机制定义了父子(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。

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

最新文章

  1. angularjs 2.0 快速案例(1)
  2. FPGA与simulink联合实时环路系列—开篇
  3. 【转】SQL Server中关于跟踪(Trace)那点事
  4. iOS远程推送之友盟Push
  5. 全局程序集GlobalAssemblyInfo.cs进行版本控制(引)
  6. 持续集成之戏说Check-in Dance
  7. Junit使用教程(一)
  8. 理解使用static import 机制(转)
  9. js中数组去重的几种方法
  10. Qt之QTemporaryFile(文件名唯一,且可以自动删除)
  11. [51nod1457]小K vs. 竹子
  12. Python爬虫之提取Bing搜索的背景图片并设置为Windows的电脑桌面
  13. 【XSY2669】归并排序 树状数组 简单组合数学
  14. RSA/SHA1加密和数字签名算法在开放平台中的应用
  15. Ubuntu 卸载重装 IntelliJ Idea Community
  16. 问题1:鼠标指向导航栏li,但li中a样式未改变
  17. sql中select into和insert into的区别
  18. &lt;&lt;Javascript Patterns&gt;&gt;阅读笔记 – 第3章 字面量和构造函数
  19. 01、Windows Store APP 设置页面横竖屏的方法
  20. python 定义二维数组

热门文章

  1. iperf安装使用教程
  2. mysql---&gt;权限管理原理和设置
  3. 科普文,搭建python开发环境
  4. Fibonacci Nim(斐波那契尼姆)游戏
  5. Jenkins环境配置(集成自动化程序)
  6. Docker基础内容之镜像构建
  7. docker 简单使用
  8. DWZ框架-- Dialog点击保存后不能自动关闭
  9. Nginx(3)---代理与负载均衡
  10. C# 接口和继承