| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 3953 人关注过本帖, 1 人收藏
标题:数据抽象
只看楼主 加入收藏
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
结帖率:94.64%
收藏(1)
已结贴  问题点数:100 回复次数:39 
数据抽象
如果你正在设计一个栈,目前这个栈只存放int数据,那么你会如何设计它呢?

其实栈的结构并不复杂,我们在头脑中就能建立一个栈的模型,当然我们在纸上把它的结构画出来也许会更好一点:
图片附件: 游客没有浏览图片的权限,请 登录注册

这张图就是一个以数组实现的栈的模型,栈底的索引是0,Pointer是栈顶指针,它指向索引5这个位置,说明现在栈里刚好有5个元素,当Pointer指向索引0时,栈为空,表示栈里已经没有元素了,当Pointer指向索引9时(这个位置不属于我们,因为它超出了范围,而且在图里也没有,但可以猜到这个位置在索引8的上一个位置,在这里我们并没有使用这块内存,仅仅只是当做一个标致),表示栈已经满了(当然我们可以动态增加,比如利用realloc)。
一个栈我想应该实现这些功能吧:
1、入栈(Push):把一个元素添加到栈顶的位置,并使栈顶指针向上移动一个位置。
2、出栈(Pop):把一个元素从栈顶取出,并使栈顶指针向下移动一个位置。
3、获得栈顶元素(Peek):把一个元素从栈顶取出,并不移动栈顶指针的位置。
注意:入栈时要检查栈是否已经满了,如果栈已经满了,输出信息提示用户,并不做任何操作。出栈和获得栈顶元素时要检查栈是否为空,如果栈为空,输出信息提示用户,不做任何操作并返回0(返回0的原因是大多时候它并不会造成一些错误,而且测试条件时也为假)。
可能还需要这样一些功能:
1、获得栈元素的数量(GetSize):这个很简单,我们只需要返回栈顶指针对应的索引值就行了,它刚好是栈元素的数量。
2、获得栈底指针(GetBottomPointer):返回这个数组的名字,它代表这个栈的底指针。
3、获得栈顶指针(GetTopPointer):返回栈顶指针即可。
2和3可以用在遍历栈时用到,比如:
程序代码:
int* bPointer = GetBottomPointer(&stack);
int* tPointer = GetTopPointer(&stack);
while (bPointer != tPointer) // (1)
    printf("%d", *bPointer++);
// or
while (tPointer != bPointer) // (2)
    printf("%d", *--tPointer);
(1)可以从栈底到栈顶遍历这个栈。
(2)可以从栈顶到栈底遍历这个栈。
看来一共有6个函数:
1、void Push(Stack*)。
2、int Pop(Stack*)。
3、int Peek(Stack*)。
4、int GetSize(Stack*)。
5、int* GetTopPointer(Stack*)。
6、int* GetBottomPointer(Stack*)。
好了,可以写代码了:
程序代码:
/**

 * Stack.h

 * Code by Kenier Lee.

 */

#ifndef _KENIER_LEE_STACK_H
#define _KENIER_LEE_STACK_H

#define STACK_SIZE 1024 /* Size of the Stack */

typedef struct StackTag Stack; /* Prepare type definition*/

struct StackTag {
   int storage[STACK_SIZE]; /* Array of int */
   int* tp;                 /* Top pointer */
};

void Push(Stack*, int);
int  Pop(Stack*);
int  Peek(Stack*);
int  GetSize(Stack*);
int* GetTopPointer(Stack*);
int* GetBottomPointer(Stack*);

#endif /* _KENIER_LEE_STACK_H */
这里栈的大小是1024,Stack里有一个字段是tp,它表示栈顶指针。
程序代码:
/**

 * Stack.c

 * Code by Kenier Lee.

 */

#include "Stack.h"
#include <stdio.h>

void InitStack(Stack* stack) {
   stack-> tp = stack->storage;
}

void Push(Stack* stack, int value) {
   int size = GetSize(stack);
   if (size == STACK_SIZE) /* Full */
      printf("Error: The stack is full.\n");
   else
      *stack->tp++ = value;
}

int Pop(Stack* stack) {
   int value = 0;
   if (stack->tp == stack->storage) /* Empty */
      printf("Error: The stack is empty.\n");
   else
      value = *--stack->tp;
   return value;
}

int Peek(Stack* stack) {
   int value = 0;
   if (stack->tp == stack->storage) /* Empty */
      printf("Error: The stack is empty.\n");
   else
      value = *stack->tp;
   return value;
}

int GetSize(Stack* stack) {
   return stack->tp - stack->storage;
}

int* GetTopPointer(Stack* stack) {
   return stack->tp;
}

int* GetBottomPointer(Stack* stack) {
   return stack->storage;
}
下面是测试代码,记得链接Stack.o
程序代码:
/**

 * StackTest.c

 * Code by Kenier Lee.

 */

#include "Stack.h"
#include <stdio.h>

int main(void) {
   Stack stack;
   int i, *tPointer, *bPointer;

   InitStack(&stack);

   for (i = 0; i < 10; ++i)
      Push(&stack, i);
   for (i = 0; i < 10; ++i)
      printf("%d ", Pop(&stack));
   printf("\n");

   for (i = 0; i < 10; ++i)
      Push(&stack, i);
   printf("Sizeof stack: %d\n", GetSize(&stack));
   tPointer = GetTopPointer(&stack);
   bPointer = GetBottomPointer(&stack);
   while (bPointer != tPointer)
      printf("%d ", *bPointer++);
   printf("\n");

   bPointer = GetBottomPointer(&stack);
   while (tPointer != bPointer)
      printf("%d ", *--tPointer);
   printf("\n");

   for (i = 0; i < 10; ++i)
      printf("%d ", Pop(&stack));
   printf("\n");
   printf("%d\n", Pop(&stack));
   printf("%d\n", Peek(&stack));

   return 0;
} /* Output:
9 8 7 6 5 4 3 2 1 0
Sizeof stack: 10
0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 0
9 8 7 6 5 4 3 2 1 0
Error: The stack is empty.
0
Error: The stack is empty.
0

*/
这样的函数调用:
InitStack(&stack);
Push(&stack, i);
Pop(&stack);
Peek(&stack);
GetSize(&stack);
GetTopPointer(&stack);
GetBottomPointer(&stack);
每个函数调用都需要传递Stack结构体的指针,更危险的是我们可以随意修改Stack里的所有字段,比如我们修改的tp字段的值,这样的修改显然是致命的,而我们无法阻止这些操作。在最开始调用任何函数之前必须先调用InitStack函数,不然可能会出现一些对话框,提示该内存不能为"write"或"read"(在Linux下面是段错误),这表示我们使用了不属于我们程序的内存,因为未初始化的tp的值是未知的。最后我们来看看Stack的C++版本:
程序代码:
/**

 * Stack.h

 * Code by Kenier Lee.

 */

#ifndef _KENIER_LEE_STACK_H
#define _KENIER_LEE_STACK_H

class Stack {
public:
   enum { STACK_SIZE = 1024 }; // Size of the Stack
   Stack();
   void push(int);
   int pop();
   int peek();
   int getSize();
   int* getTopPointer();
   int* getBottomPointer();
private:
   int storage[STACK_SIZE]; // Array of int
   int* tp; // Top pointer
};

#endif // _KENIER_LEE_STACK_H
不需要typedef,我们也不能直接修改tp的值了,因为它是私有的,我们能使用的只有从public:到private:之前的名字。
程序代码:
/**

 * Stack.cpp

 * Code by Kenier Lee.

 */

#include "Stack.h"
#include <iostream>
using namespace std;

Stack::Stack() {
   tp = storage;
}

void Stack::push(int value) {
   int size = getSize();
   if (size == STACK_SIZE) /* Full */
      cout << "Error: The stack is full." << endl;
   else
      *tp++ = value;
}

int Stack::pop() {
   int value = 0;
   if (tp == storage) /* Empty */
      cout << "Error: The stack is empty." << endl;
   else
      value = *--tp;
   return value;
}

int Stack::peek() {
   int value = 0;
   if (tp == storage) /* Empty */
      cout << "Error: The stack is empty." << endl;
   else
      value = *tp;
   return value;
}

int Stack::getSize() {
   return tp - storage;
}

int* Stack::getTopPointer() {
   return tp;
}

int* Stack::getBottomPointer() {
   return storage;
}
这是定义之前声明的那些成员函数,其中还有一个构造函数Stack(),它与类名相同,在创建Stack对象时它将被自动调用,它可以替代InitStack,而且比InitStack好,因为不用担心它不会被调用。
程序代码:
/**

 * StackTest.cpp

 * Code by Kenier Lee.

 */

#include "Stack.h"
#include <iostream>
using namespace std;

int main() {
   Stack stack; // Constructor called
   int i, *tPointer, *bPointer;

   for (i = 0; i < 10; ++i)
      stack.push(i);
   for (i = 0; i < 10; ++i)
      cout << stack.pop() << " ";
   cout << endl;

   for (i = 0; i < 10; ++i)
      stack.push(i);
   cout << "Sizeof stack: " << stack.getSize() << endl;
   tPointer = stack.getTopPointer();
   bPointer = stack.getBottomPointer();
   while (bPointer != tPointer)
      cout << *bPointer++ << " ";
   cout << endl;

   bPointer = stack.getBottomPointer();
   while (tPointer != bPointer)
      cout << *--tPointer << " ";
   cout << endl;

   for (i = 0; i < 10; ++i)
      cout << stack.pop() << " ";
   cout << endl;
   cout << stack.pop() << endl;
   cout << stack.peek() << endl;
} /* Output:
9 8 7 6 5 4 3 2 1 0
Sizeof stack: 10
0 1 2 3 4 5 6 7 8 9
9 8 7 6 5 4 3 2 1 0
9 8 7 6 5 4 3 2 1 0
Error: The stack is empty.
0
Error: The stack is empty.
0

*/
现在我们又看到了这样的调用:
stack.push(i);
stack.pop();
stack.peek();
stack.getSize();
stack.getTopPointer();
stack.getBottomPointer();
我感觉这样要清晰得多,很容易看出这些操作是指对于那个对象的,并且如果你现在写这样的代码:stack.tp++ 或 stack.tp = 0 ... 等等修改tp字段值的操作都会被编译器看作是错误,因为tp被声明为私有的(只能在这个类的成员函数中使用),这样就安全多了。Stack stack;这句话被执行的时候,该类的构造函数Stack()就自动调用了,它完成了初始化的工作,不用担心初始化会被遗忘。

C++提供了一种,把函数与自定义数据类型绑定在一起的特性,这被称做数据抽象,而且上面的代码量明显要比前一个C版本的要少,语法更清楚,数据抽象的用途远不止这些,更多的特性,如果以后我有时间再与大家一一分享吧!

不知道看了这些代码之后同鞋们有没有兴趣去学习 C Plus Plus呢?

[ 本帖最后由 lz1091914999 于 2012-2-6 22:10 编辑 ]
搜索更多相关主题的帖子: 模型 设计 如何 元素 
2012-02-06 21:34
vandychan
Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15
等 级:贵宾
威 望:18
帖 子:2296
专家分:6418
注 册:2010-8-20
收藏
得分:13 
膜拜一下

到底是“出来混迟早要还”还是“杀人放火金腰带”?
2012-02-06 21:56
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
收藏
得分:13 
呵呵,写的不错。从面向过程转向面向对象是个必然的过程,上大学时用过两年C plus plus,后来.net异军突起,而C sharp优美的语言结构深深地吸引了我,一直到现在。
java出现的比C sharp早,但一直没关注。这些天在玩android,用java。因为C sharp语法源自C++和java,所以学习和使用java并不困难,只是觉得有点别扭,不太习惯。

重剑无锋,大巧不工
2012-02-06 22:09
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
收藏
得分:0 
回复 2楼 vandychan
向vandychan版主打个招呼,你又可以尽情打击作业贴了

重剑无锋,大巧不工
2012-02-06 22:12
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
回复 3楼 beyondyf
我开始也是学的C,之后又研究了Java,发现抽象换来的是简单和清晰,但在效率上会有所损失,但学了C++之后,又发现抽象不仅换来了简单和清晰,在效率上与C也相差无几。所以以后我还是用C++吧,我准备把英文学好后去学Qt,杨大哥有性趣吗?

My life is brilliant
2012-02-06 22:18
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
收藏
得分:0 
回复 5楼 lz1091914999
谢谢了,我不用C++已经很久了。个人其实不太喜欢C++,语法有些复杂模糊、安全性不足很容易出问题、是面向过程与面向对象的混合体,我喜欢纯粹的OOP语言。
这只是个人的倾向,并不反对别人热爱C++。
我们可以在设计模式这个层面交流探讨,虽然我不写C++代码,但看还是没问题的。

重剑无锋,大巧不工
2012-02-06 22:30
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
回复 6楼 beyondyf
恩,其实我是一个比追求效率的人吧,虽然Java很方便,而且语法的清晰程度也令人痴迷,但是我并不愿意牺牲效率去换得这些,当然利用Java做一些小工具也是不错的。
其实我一点也不懂设计模式,只是看了Bruce(看我的头像)的书后觉得这方面很有用。以后杨大哥想做Android的开发吗?

My life is brilliant
2012-02-06 22:41
beyondyf
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
等 级:贵宾
威 望:103
帖 子:3282
专家分:12654
注 册:2008-1-21
收藏
得分:0 
只是玩而已,为此刚换了手机,换了部三星9250,全球一第部android4.0手机,google定制原生系统
java的效率并不是语言本身的性质造成的,而因为它被设计成一种解释型中间语言,牺牲了效率它所得到的是跨平台使用的特性。
我也很看重效率,不过要分地方。比如说做界面,用C#、java显示也许耗时20毫秒,但构建起来非常简单;用C或C++也许0毫秒显示,但那代码量看着很让人头大。虽然IDE可以帮些忙,但想用好MFC库这个庞然大物没几年功力是不行的。而对于我们看窗口显示,快慢个20毫秒有什么关系。
所以,我的程序大部分用C#构建,只在关键算法和数据分析方面用C语言写成动态链接库,再由C#封装后使用。

重剑无锋,大巧不工
2012-02-06 23:13
pangding
Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19Rank: 19
来 自:北京
等 级:贵宾
威 望:94
帖 子:6784
专家分:16751
注 册:2008-12-20
收藏
得分:13 
回复 8楼 beyondyf
android 我也想学学。入门有什么推荐的资料吗?
2012-02-06 23:29
lz1091914999
Rank: 14Rank: 14Rank: 14Rank: 14
来 自:四川
等 级:贵宾
威 望:37
帖 子:2011
专家分:5959
注 册:2010-11-1
收藏
得分:0 
回复 8楼 beyondyf
恩,不过Qt比MFC好太多了,你可以去了解一下,我可买不起你那手机,毕竟我现在还在用爸爸妈妈的钱呢!

My life is brilliant
2012-02-06 23:37
快速回复:数据抽象
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.025935 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved