抽象数据类型(abstract data type, ADT),是以通用的方式描述数据类型,而没有引入语言或实现细节。
程序员常常通过定义类,来表示更通用的概念,而对于抽象数据类型,使用类是一种非常好的方式。
当类概念使用ADT方法时,一般是这样做:
①私有部分必须表示数据存储的方式。例如,可以使用常规数组、动态分配数组或更高级的数据结构(如链表);
②公有接口应隐藏数据表示,而以通用的术语来表达,如创建栈、压入等。
例如代码,这是一个栈的表示方式(注意注释):
typedef unsigned long Item; //Item作为unsigned long的别名
class Stack
{
private:
enum {Max=5}; //创建枚举常量
Item items[Max]; //Item是unsigned long的别名,见上面
int top;
public:
Stack();
bool isempty()const; //用于检测是否为空
bool isfull()const; //是否为满
bool push(const Item&item); //放入
bool pop(Item&item); //取出
};
Stack::Stack() //默认构造函数,初始化top为0,top用于计数当前指针指向数组下一次填充时的位置(即如果有一个成员,为items[0],此时top=1
{
top = 0;
}
bool Stack::isempty()const //调用函数,询问是否为空,如果top=0(指向0号位置,说明数组里没有东西),那么top==0为真(返回true),如果top>0(说明数组里至少有一个东西),那么top==0为假(返回false)。
{
return top == 0;
}
bool Stack::isfull()const //调用函数,询问是否满了。如果top=5(说明0~4都有成员),于是top==Max为真(返回true,这里表示满了)相反,则返回false,说明没满
{
return top == Max;
}
bool Stack::push(const Item&item) //调用函数,用于放置
{
if (top < Max) //如果没满(原理上面已经解释)
{
items[top++] = item; //新内容被放置到items数组里,位置为top(因为top指向新内容放置的数组位置),然后top+1
return true; //返回true,说明放置成功
}
else return false; //否则返回false,说明放置失败
}
bool Stack::pop(Item&item)
{
if (top > 0) //如果top>0,说明至少有一个元素
{
item = items[--top]; //传递的参数等于top-1后(也就是说上一次放置的位置),相当于把里面的内容取出,并赋值给传递的参数(注意,参数是引用)
return true; //返回true,说明取出成功
}
else return false; //否则返回false,取出失败
}
总结:
①Stack::push()函数,是将内容放置在私有成员的items数组中,然后通过更改top的值,移动指针。
②调用isfull()和isempty()函数,可以检测这里的栈是否已满。
③top用于指向下一个放置新成员的位置。假如当前有0个成员,他指向0(因为items[0]是空的),有2个成员,他指向2(因为items[0]和items[1]有内容,而items[2]是空的)。
④items[Max]这个数组,用于储存数据;
⑤Item这个类型名,实际上是unsigned long的别名。
⑥push()和pop()函数,分别对应的是放置和取出。并返回是否成功,参数是放置和取出的对象,因此用的是引用。
⑦取出时,实际上并没有删除数组成员的数据,而是改变指向的位置(top的值),假如下一次放置新数据,那么就会覆盖之前的。
⑧默认构造函数使得创建一个新的对象时,其top为0(即指向最初的位置)。
⑨由于Item是unsigned long的别名,因此他一般用作储存整型的数据(浮点类型可能会出现丢失数据的问题。
下面是一个添加了显示名字,并进行实际应用的代码:
//1.h 头文件,用于储存类定义
#include<string>
#pragma once
typedef unsigned long Item; //Item作为unsigned long的别名
struct shuiguo
{
std::string name;
unsigned long m;
};
class Stack
{
private:
enum { Max = 5 }; //创建枚举常量
std::string name[Max];
Item items[Max]; //Item是unsigned long的别名,见上面
int top;
public:
Stack();
bool isempty()const; //用于检测是否为空
bool isfull()const; //是否为满
bool push(const shuiguo&); //放入
bool pop(shuiguo&); //取出
};
//1.cpp 主程序所在源代码文件
#include<iostream>
#include<string>
#include<Windows.h>
#include"1.h"
int main()
{
using namespace std;
Stack chuwugui; //创建对象储物柜
cout << "这里是一个储物箱,你是否要存/取东西呢?" << endl;
cout << "a.存\tp.取\tq.退出\n我的选择是:";
char choice;
while (cin >> choice&&choice != 'q')
{
while (!isalpha(choice)) //如果输入的不是字母,清除输入缓存,并提示重新输入,然后重新开始循环
{
cout << "请输入a.存\tp.取\tq.退出";
cin.sync();
continue;
}
cin.sync(); //清除输入缓存,只读取第一个输入的字母
switch (choice) //根据输入的不同,执行不同的选项
{
case 'a': //输入大写小写的a都是这里
case 'A':
if (chuwugui.isfull()) //如果满的话,返回提示信息
{
cout << "已经满啦!无法继续存放东西了,你需要取出一些东西才可以。" << endl;
break;
}
else //否则可以存放东西
{
shuiguo one; //创建结构,用于存储存放的东西
cout << "请输入你要存放的东西的名称:";
getline(cin, one.name);
cout << "请输入你要存放的东西的数量:";
cin >> one.m;
cin.sync(); //清除输入缓存,防止干扰
if (chuwugui.push(one))
{
cout << "存放成功!" << one.name << " 共计 " << one.m << " 个已经被放入储物柜啦!" << endl;
break;
}
else
{
cout << "由于某种未知的原因,存放失败了。。。。" << endl;
break;
}
}
case 'p':
case 'P':
if (chuwugui.isempty())
{
cout << "很抱歉,储物柜里面已经空空如也了,你不可能再从里面取出什么东西了。" << endl;
cout << "等等!你难道要抢走这个萌萌哒的储物箱吗?!警察叔叔!!!~~~" << endl;
break;
}
else
{
shuiguo one;
cout << "现在帮你取东西~~亲~请稍微等待一秒钟。。。" << endl;
Sleep(1000);
if (chuwugui.pop(one))
{
cout << "取出来啦!" << endl;
cout << "取出来的东西是 " << one.name << " ,一共有 " << one.m << " 个~~~喏!给你!" << endl;
if (chuwugui.isempty())
{
cout << "另外呢~~不得不告诉你一声,储物柜里面已经空空如也啦~~" << endl;
break;
}
break;
}
else
{
cout << "因为某种未知的错误,什么都没取出来!怎么会这样?" << endl;
break;
}
}
default:cout << "可以输入 a 或者 p 或者 q 嘛?你乱输入的话,我是不会知道你想干嘛的!" << endl;
break;
}
cout << endl;
cout << "口←一个萌萌哒的储物箱,你是否要存/取东西呢?" << endl;
cout << "a.存\tp.取\tq.退出\n我的选择是:";
}
cout << endl << "Bye~~~" << endl;
system("pause");
return 0;
}
//2.cpp 存放类的成员函数定义
#include<iostream>
#include<string>
#include"1.h"
Stack::Stack() //默认构造函数,初始化top为0,top用于计数当前指针指向数组下一次填充时的位置(即如果有一个成员,为items[0],此时top=1
{
top = 0;
}
bool Stack::isempty()const //调用函数,询问是否为空,如果top=0(指向0号位置,说明数组里没有东西),那么top==0为真(返回true),如果top>0(说明数组里至少有一个东西),那么top==0为假(返回false)。
{
return top == 0;
}
bool Stack::isfull()const //调用函数,询问是否满了。如果top=5(说明0~4都有成员),于是top==Max为真(返回true,这里表示满了)相反,则返回false,说明没满
{
return top == Max;
}
bool Stack::push(const shuiguo&x) //调用函数,用于放置
{
if (top < Max) //如果没满(原理上面已经解释)
{
name[top] = x.name;
items[top++] = x.m; //新内容被放置到items数组里,位置为top(因为top指向新内容放置的数组位置),然后top+1
return true; //返回true,说明放置成功
}
else return false; //否则返回false,说明放置失败
}
bool Stack::pop(shuiguo&x)
{
if (top > 0) //如果top>0,说明至少有一个元素
{
x.m = items[--top]; //传递的参数等于top-1后(也就是说上一次放置的位置),相当于把里面的内容取出,并赋值给传递的参数(注意,参数是引用)
x.name = name[top];
return true; //返回true,说明取出成功
}
else return false; //否则返回false,取出失败
}
输出:
这里是一个储物箱,你是否要存/取东西呢?
a.存 p.取 q.退出
我的选择是:a
请输入你要存放的东西的名称:苹果
请输入你要存放的东西的数量:1
存放成功!苹果 共计 1 个已经被放入储物柜啦!
口←一个萌萌哒的储物箱,你是否要存/取东西呢?
a.存 p.取 q.退出
我的选择是:a
请输入你要存放的东西的名称:梨子
请输入你要存放的东西的数量:5
存放成功!梨子 共计 5 个已经被放入储物柜啦!
口←一个萌萌哒的储物箱,你是否要存/取东西呢?
a.存 p.取 q.退出
我的选择是:A
请输入你要存放的东西的名称:香 蕉
请输入你要存放的东西的数量:3
存放成功!香 蕉 共计 3 个已经被放入储物柜啦!
口←一个萌萌哒的储物箱,你是否要存/取东西呢?
a.存 p.取 q.退出
我的选择是:p
现在帮你取东西~~亲~请稍微等待一秒钟。。。
取出来啦!
取出来的东西是 香 蕉 ,一共有 3 个~~~喏!给你!
口←一个萌萌哒的储物箱,你是否要存/取东西呢?
a.存 p.取 q.退出
我的选择是:p
现在帮你取东西~~亲~请稍微等待一秒钟。。。
取出来啦!
取出来的东西是 梨子 ,一共有 5 个~~~喏!给你!
口←一个萌萌哒的储物箱,你是否要存/取东西呢?
a.存 p.取 q.退出
我的选择是:p
现在帮你取东西~~亲~请稍微等待一秒钟。。。
取出来啦!
取出来的东西是 苹果 ,一共有 1 个~~~喏!给你!
另外呢~~不得不告诉你一声,储物柜里面已经空空如也啦~~
口←一个萌萌哒的储物箱,你是否要存/取东西呢?
a.存 p.取 q.退出
我的选择是:p
很抱歉,储物柜里面已经空空如也了,你不可能再从里面取出什么东西了。
等等!你难道要抢走这个萌萌哒的储物箱吗?!警察叔叔!!!~~~
口←一个萌萌哒的储物箱,你是否要存/取东西呢?
a.存 p.取 q.退出
我的选择是:q
Bye~~~
请按任意键继续. . .