数据抽象与封装 一一对象与类
数据抽象与封装 --对象与类
主要内容 ■ 数据抽象与封装概述 面向对象程序设计概述 对象和类 ■ 对象的访问控制 对象的初始化和消亡前处理 ■ 对常量对象的访问:常(const)成员 同类对象间的数据共享:静态(static)成员 提高对对象私有数据的访问效率:友元 (friend 类作为模块
主要内容 ◼ 数据抽象与封装概述 ◼ 面向对象程序设计概述 ◼ 对象和类 ◼ 对象的访问控制 ◼ 对象的初始化和消亡前处理 ◼ 对常量对象的访问:常(const)成员 ◼ 同类对象间的数据共享:静态(static)成员 ◼ 提高对对象私有数据的访问效率:友元 (friend) ◼ 类作为模块
数据抽象与封装 ■ 数据抽象 数据的使用者只需要知道对数据所能实施的操作以及 这些操作之间的关系,而不必知道数据的具体表示形 式。 数据封装 把数据及其操作作为一个整体来进行实现 数据的具体表示对使用者是不可见的(封装),对数 据的访问(使用)只能通过封装体所提供的对外接口 (允许的操作)来完成。 抽象强调的是外部行为; 封装强调的是内部实现。 数据抽象与封装是面向对象程序设计的基础
数据抽象与封装 ◼ 数据抽象 • 数据的使用者只需要知道对数据所能实施的操作以及 这些操作之间的关系,而不必知道数据的具体表示形 式。 ◼ 数据封装 • 把数据及其操作作为一个整体来进行实现。 • 数据的具体表示对使用者是不可见的(封装),对数 据的访问(使用)只能通过封装体所提供的对外接口 (允许的操作)来完成。 ◼ 抽象强调的是外部行为;封装强调的是内部实现。 ◼ 数据抽象与封装是面向对象程序设计的基础
例:“栈”数据的实现 栈是一种由若干个具有线性次序关系的元素所 构成的复合数据。对栈只能实施两种操作: 进栈(push):往栈中增加一个元素 ·退栈(pop):从栈中删除一个元素 。上述两个操作满足一个重要性质: push(...);...pop(...);...;push(x);pop(y); X==y 即,后进先出 (Last In First Out, 简称LIFO) 从数据抽象的角度,栈的使用者只需要知道上 面的这些信息,不需要知道栈是如何实现的 数组或链表等)
例:“栈”数据的实现 ◼ 栈是一种由若干个具有线性次序关系的元素所 构成的复合数据。对栈只能实施两种操作: • 进栈(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]; buffer: top
“栈”数据的实现 --非数据抽象和封装途径 ◼ 定义栈数据类型 const int STACK_SIZE=100; struct Stack { int top; int buffer[STACK_SIZE]; }; top buffer:
直接操作栈数据 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--;
◼ 直接操作栈数据 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;
• 存在的问题 ◼ 操作必需知道数据的表示,数据表示发生变化将影 响操作。 ◼ 麻烦并易产生误操作,因此不安全。例如,对于进 栈操作: st.top--; //书写失误导致误操作 st.buffer[st.top] = 12; ◼ 忘了初始化: st.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 init(Stack &s) } s.top =-1; void pop(Stack &s,int &i) if (s.top ==-1) {cout<<“Stack is empty.n”y exit(-1); else i s.buffer[s.top];s.top--; return;
◼ 通过过程抽象使用栈数据 • 先预定义三个函数 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; } } void init(Stack &s) { s.top = -1; }
·利用预定义的函数使用栈 Stack st;/定义栈数据 int x; init(st);/对st进行初始化。 push(st,12);/把12放进栈。 11nn·0 pop(st,x);/把栈顶元素退栈并存入变量x
• 利用预定义的函数使用栈 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);
• 存在的问题 ◼ 数据类型的定义与操作的定义是分开的,二者之间 没有显式的联系,push、pop在形式上与下面的函 数f没有区别,函数f也能作用于st: void f(Stack &s) { ...... } f(st); //操作st之后,st可能不再是一个“栈”了! ◼ 数据表示仍然是公开的,无法防止使用者直接操作 栈数据,因此也会面临直接操作栈数据所带来的问 题: st.top--; st.buffer[st.top] = 12; ◼ 忘了初始化:init(st);