10.7 抽象数据类型

Stock类非常具体。然而,程序员常常通过定义类来表示更通用的概念。例如,就实现计算机专家们所说的抽象数据类型(abstract data type,ADT)而言,使用类是一种非常好的方式。顾名思义,ADT以通用的方式描述数据类型,而没有引入语言或实现细节。例如,通过使用栈,可以以这样的方式存储数据,即总是从堆顶添加或删除数据。例如,C++程序使用栈来管理自动变量。当新的自动变量被生成后,它们被添加到栈项;消亡时,从栈中删除它们。

下面简要地介绍一下栈的特征。首先,栈存储了多个数据项(该特征使得栈成为一个容器——一种更为通用的抽象);其次,栈由可对它执行的操作来描述。

可以将上述描述转换为一个类声明,其中公有成员函数提供了表示栈操作的接口,而私有数据成员负责存储栈数据。类概念非常适合于ADT方法。

私有部分必须表明数据存储的方式。例如,可以使用常规数组、动态分配数组或更高级的数据结构(如链表)。然而,公有接口应隐藏数据表示,而以通用的术语来表达,如创建栈、压入等。程序清单10.10演示了一种方法,它假设系统实现了bool类型。如果您使用的系统没有实现,可以使用int、0和1代替bool,false和true。

程序清单10.10 stack.h

// stack.h -- 用于表示抽象数据类型——栈的类定义
#ifndef STACK_H_
#define STACK_H_

typedef unsigned long Item;

class Stack
{
private:
    enum {MAX = 10};    // 常量
    Item items[MAX];    // 保存数据项的数组
    int top;            // 栈顶数据项的索引
public:
    Stack();
    bool isempty() const;
    bool isfull() const;
    // 如果栈已满,则push()方法返回falses,否则返回true
    bool push(const Item & item);   // 在栈中添加数据项
    // 如果栈为空,则pop()方法返回false,否则返回true
    bool pop(Item & item);          // 从栈顶删除数据
};
#endif

在程序清单10.10所示的示例中,私有部分表明,栈是使用数组实现的;而公有部分隐藏了这一点。因此,可以使用动态数组来代替数组,而不会改变类的接口。这意味着修改栈的实现后,不需要重新编写使用栈的程序,而只需重新编译栈代码,并将其与已有的程序代码链接起来即可。

接口是冗余的,因为pop()和push()返回有关栈状态的信息(满或空),而不是void类型。在如何处理超出栈限制或者清空栈方面,这为程序员提供了两种选择。他可以在修改栈前使用isempty()和isfull()来查看,也可以使用push()和pop()的返回值来确定操作是否成功。

这个类不是根据特定的类型来定义栈,而是根据通用的Item类型来描述。在这个例子中,头文件使用typedef用Item代替unsigned long。如果需要double栈或结构类型的栈,则只需修改typedef语句,而类声明和方法定义保持不变。类模板(参见第14章)提供了功能更强大的方法,来将存储的数据类型与类设计隔离开来。

接下来需要实现类方法,程序清单10.11提供了一种可行的实现。

程序清单10.11 stack.cpp

// stack.cpp -- Stack成员函数
#include "stack.h"
Stack::Stack()    // 创建一个空栈
{
    top = 0;
}

bool Stack::isempty() const
{
    return top == 0;
}

bool Stack::isfull() const
{
    return top == MAX;
}

bool Stack::push(const Item & item) 
{
    if (top < MAX)
    {
        items[top++] = item;
        return true;
    }
    else
        return false;
}

bool Stack::pop(Item & item)
{
    if (top > 0)
    {
        item = items[--top];
        return true;
    }
    else
        return false; 
}

默认构造函数确保所有栈被创建时都为空。pop()和push()的代码确保栈顶被正确处理。这种保证措施是OOP更为可靠的原因之一。假设要创建一个独立数组来表示栈,创建一个独立变量来表示栈顶索引。则每次创建新栈时,都必须确保代码是正确的。没有私有数据提供的保护,则很可能由于无意修改了数据二导致程序出现非常严重的故障。

下面来测试该栈。程序清单10.12模拟了售货员的行为——使用栈的后进先出方式,从购物筐的最上面开始处理购物订单。

程序清单10.12 stacker.cpp

// stacker.cpp -- 测试Stack类
#include <iostream>
#include <cctype>  // or ctype.h
#include "stack.h"
int main()
{
    using namespace std;
    Stack st; // 创建一个空栈
    char ch;
    unsigned long po;
    cout << "Please enter A to add a purchase order,\n"
        << "P to process a PO, or Q to quit.\n";
    while (cin >> ch && toupper(ch) != 'Q')
    {
        while (cin.get() != '\n')   
            continue;
        if (!isalpha(ch))
        {
            cout << '\a';
            continue;
        }
        switch(ch)
        {
             case 'A':
             case 'a': cout << "Enter a PO number to add: ";
                       cin >> po;
                       if (st.isfull())
                           cout << "stack already full\n";
                       else
                           st.push(po);
                       break;
             case 'P':
             case 'p': if (st.isempty())
                           cout << "stack already empty\n";
                       else {
                           st.pop(po);
                           cout << "PO #" << po << " popped\n";
                       }
                       break;
        }
        cout << "Please enter A to add a purchase order,\n"
             << "P to process a PO, or Q to quit.\n";
    }
    cout << "Bye\n";
    return 0; 
}

程序清单10.12中的while循环删除输入行中剩余部分,就现在而言这并非是必不可少的,但它使程序的修改更方便(第14章将对这个程序进行修改)。下面是该程序的运行情况:

Please enter A to add a purchase order,
P to process a PO, or Q to quit.
A
Enter a PO number to add; 17885
Please enter A to add a purchase order,
P to process PO, or Q to quit.
P
PO #17885 popped
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
Enter a PO number to add: 17965
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
A
Enter a PO number to add: 18002
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
P
PO #18002 popped
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
P
PO #17965 popped
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
P
stack already empty
Please enter A to add a purchase order,
P to process a PO, or Q to quit.
Q
Bye
文件下载(已下载 478 次)

发布时间:2014/7/9 9:44:11  阅读次数:3382

2006 - 2024,推荐分辨率1024*768以上,推荐浏览器Chrome、Edge等现代浏览器,截止2021年12月5日的访问次数:1872万9823 站长邮箱

沪ICP备18037240号-1

沪公网安备 31011002002865号