FP-01 数据抽象与封装概述

Lecture 01 数据抽象与封装概述

抽象与封装

抽象与封装是两个重要的程序设计手段,主要是用来驾驭程序的复杂度,便于大型程序的设计、理解与维护。

对于一个程序实体而言,

  • 抽象是指该程序实体的外部可观察到的行为,不考虑该程序实体的内部是如何实现的。(控制复杂度)
  • 封装是指把该程序实体内部的具体实现细节对使用者隐藏起来,只对外提供一个接口。(信息保护)

主要的程序抽象与封装机制包括:

  • 过程抽象与封装
  • 数据抽象与封装

过程抽象与封装

过程抽象

  • 用一个名字来代表一段完成一定功能的程序代码,代码的使用者只需要知道代码的名字以及相应的功能,而不需要知道对应的程序代码是如何实现的。

过程封装

  • 把命名代码的具体实现隐藏起来(对使用者不可见,或不可直接访问),使用者只能通过代码名字来使用相应的代码。
  • 命名代码所需要的数据是通过参数(或全局变量)来获得,计算结果通过返回机制(或全局变量)返回。

实现过程抽象与封装的程序实体通常称为子程序。在C/C++语言中,子程序用函数来表示。

过程抽象与封装是基于功能分解与复合的过程式程序设计的基础。

数据抽象与封装

数据抽象

  • 只描述对数据能实施哪些操作以及这些操作之间的关系,数据的使用者不需要知道数据的具体表示形式。

数据封装

  • 把数据及其操作作为一个整体来进行实现,其中,数据的具体表示被隐藏起来(使用者不可见,或不可直接访问),对数据的访问(使用)只能通过提供的操作(对外接口)来完成。

与过程抽象与封装相比,数据抽象与封装能够实现更好的数据保护。

数据抽象与封装是面向对象程序设计的基础。

“栈”数据的表示与操作(Example)

是一种由若干个具有线性次序关系的元素所构成的复合数据。对栈只能实施两种操作:

  • 进栈(push):往栈中增加一个元素
  • 退栈(pop):从栈中删除一个元素
  • 上述两个操作满足一个重要性质:
    • push(…); …pop(…); … ;push(x);pop(y); …
    • x == y
  • 即,增加/删除操作在线性序列的同一端进行(后进先出,Last In First Out,简称LIFO)

从数据抽象的角度,栈的使用者只需要知道上面的这些信息,不需要知道栈是如何实现的(数组或链表等)。

“栈”的实现 – 非数据抽象和封装途径

定义一个数据类型来表示栈数据

const int STACK_SIZE=100;
struct Stack {	
    int top;
    int buffer[STACK_SIZE];
};
直接操作栈数据
Stack st; //定义栈数据
st.top = -1; //对st进行初始化
//把12放进栈
if (st.top == STACK_SIZE-1) { 
    cout << “Stack is overflow.\n”; exit(-1); 
}
st.top++; 
st.buffer[st.top] = 12; 
......
//把栈顶元素退栈并存入变量x
if (st.top == -1) { 
    cout << “Stack is empty.\n”; exit(-1); 
}
int x = st.buffer[st.top]; 
st.top--;

存在的问题

  • 操作必需知道数据的具体表示形式。
  • 数据表示形式发生变化将会影响操作。

麻烦并易产生误操作,因此不安全。例如,把进栈操作误写成:

st.top--; //书写失误导致误操作
st.buffer[st.top] = 12;
  • 忘了初始化:
st.top = -1;
通过过程抽象与封装操作栈数据

先预定义三个函数

void init(Stack &s){   
    s.top = -1;
}

void push(Stack &s, int i){	
    if (s.top == STACK_SIZE-1) {	
        cout << “Stack is overflow.\n”;
		exit(-1);
	}
	else{	
        s.top++; s.buffer[s.top] = i;
		return;
	}
}
void pop(Stack &s, int &i){
    if (s.top == -1) {
        cout <<“Stack is empty.\n”;
		exit(-1);
	}
  	else {
        i = s.buffer[s.top]; s.top--;
		return;
	}
}

利用预定义的函数操作栈数据

Stack st; //定义栈数据
int x;
init(st);  //对st进行初始化。
push(st,12);  //把12放进栈。
......
pop(st,x);  //把栈顶元素退栈并存入变量x。

存在的问题

  • 数据类型的定义与操作的定义是分开的,二者之间没有显式的联系,push、pop在形式上与下面的函数f没有区别,函数f也能作用于st:

    void f(Stack &s) { ...... }
    f(st); //操作st之后,st可能不再是一个“栈”了!
    
  • 数据表示仍然是公开的,无法防止使用者直接操作栈数据,因此也会面临直接操作栈数据所带来的问题:

    st.top--; 
    st.buffer[st.top] = 12;
    
  • 忘了初始化:

    init(st);
    

“栈”的实现 – 数据抽象和封装途径

定义栈数据类型

const int STACK_SIZE=100;
class Stack {	 
public: //对外的接口(外部可使用的内容)
	Stack();
   void push(int i);
   void pop(int &i);
private: //隐藏的内容,外部不可使用
   int top;
   int buffer[STACK_SIZE];
};
Stack::Stack(){
    top = -1; 
}

void Stack::push(int i){
    if (top == STACK_SIZE-1) {
        cout << “Stack is overflow.\n”;
		exit(-1);
	}
	else{
        top++;
        buffer[top] = i;
	    return;
	}
}
	
void Stack::pop(int &i){ 
    if (top == -1) {	
        cout << “Stack is empty.\n”;
		exit(-1);
	}
	else {	
        i = buffer[top]; top--;
		return;
	}
}

使用栈类型数据

Stack st;  //会自动地去调用st.Stack()对st进行初始化。
int x;
st.push(12);  //把12放进栈st。
......
st.pop(x);  //把栈顶元素退栈并存入变量x。
......
st.top = -1;  //Error
st.top++;  //Error
st.buffer[st.top] = 12;  //Error
st.f(); //Error

“栈”的另一种实现(链表表示)-- 数据抽象和封装途径

class Stack{	
public:
    Stack();
	void push(int i);
	void pop(int &i);
private:
	struct Node { 
        int content;
		Node *next;
	} *top;
};
Stack::Stack() { 
    top = NULL; 
}

void Stack::push(int i) {
    Node *p=new Node;
	if (p == NULL) {
        cout << "Stack is overflow.\n";
		exit(-1);
	}
	else {
        p->content = i;
		p->next = top;	top = p;
		return;
	}
}

void Stack::pop(int &i) {
    if (top == NULL) {
        cout << "Stack is empty.\n";
		exit(-1);
	}
	else {
        Node *p=top;
		top = top->next;
		i = p->content;
		delete p;
		return;
	}
}

改变栈类型数据的表示对使用者没有影响!

Stack st; 
int x;
st.push(12);
st.pop(x);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值