数据抽象 --对象与类 Copyright@ 陈家骏老师.

Slides:



Advertisements
Similar presentations
第四章 类 和 对 象.
Advertisements

课程 要求 参考 书目 课程 内容 课程 练习.
7.2 访问控制 —— 公有继承 公有继承练习 //Point.h #ifndef _POINT_H #define _POINT_H class Point { //基类Point类的定义 public: //公有函数成员 void initPoint(float x = 0, float.
第14章 c++中的代码重用.
第10讲 Java面向对象编程基础(4) 教学目标 主要内容.
Using C++ The Weird Way Something about c++11 & OOP tricks
EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则
内容提要 对象的生命周期 构造函数 析构函数 拷贝构造函数. 常宝宝 北京大学计算机科学与技术系
Chap 3 堆疊與佇列 Stack and Queue.
第六章 继承性和派生类 胡昊 南京大学计算机系软件所.
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第三章 C++中的C 面向对象程序设计(C++).
授课老师:龚涛 信息科学与技术学院 2018年4月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
线性表小结 元素之间的线性关系 顺序表 顺序表:元素相邻存储 单链表:后继指针链接 一维数组 给定下标随机存取
cn/~dongeliu/dsa.html 刘 东 信息学院6系 中国科学技术大学
第11讲 类的继承 1. 类的继承的概念 2. 类的单继承机制 3. 单继承中的构造函数和析构函数.
第12讲 多继承与虚基类 多继承 虚基类.
辅导课程六.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
第三章 栈与队列 £3.1 栈 £3.3 队列 £3.2 栈的应用举例 £3.1.1 栈的定义 £3.1.2 栈的顺序存储结构
第3章 栈和队列(一).
第三章 栈和队列.
第六章 数据抽象-类 胡昊 南京大学计算机系软件所.
泛型委托 泛型接口、方法和委托.
第三章 栈和队列.
C/C++/Java 哪些值不是头等程序对象
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
第七章 操作符重载 胡昊 南京大学计算机系软件所.
第三章 链表 单链表 循环链表 多项式及其相加 双向链表 稀疏矩阵.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
常宝宝 北京大学计算机科学与技术系 数据结构(三) 常宝宝 北京大学计算机科学与技术系
第五章 递归与广义表 递归的概念 递归过程与递归工作栈 递归与回溯 广义表.
EBNF与操作语义 请用扩展的 BNF 描述 javascript语言里语句的结构;并用操作语义的方法描述对应的语义规则
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
第11讲 类的继承 1. 类的继承的概念 2. 类的单继承机制 3. 单继承中的构造函数和析构函数.
$9 泛型基础.
C++复习3 ----类的继承与派生.
C#面向对象程序设计 $6 深入理解类.
Classes (1) Lecture 6.
第10讲 构造函数和析构函数 构造函数 析构函数 This 指针.
C++复习2----类与对象.
第四章 栈和队列 栈 ( Stack ) 队列 ( Queue ) 优先队列 (Priority Queue) 小结.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
字符串 (String) 字符串是 n (  0 ) 个字符的有限序列, 记作 S = “c1c2c3…cn” 其中,S 是串名字
本节内容 类成员的访问控制 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
第二章 Java语法基础.
第 六 讲 栈和队列(一).
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
辅导课程十五.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
#include <iostream.h>
第二章 Java基本语法 讲师:复凡.
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本节内容 进程 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本节内容 this指针 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
Presentation transcript:

数据抽象 --对象与类 Copyright@ 陈家骏老师

主要内容 数据抽象与封装 类和对象 对象的初始化和消亡前处理

数据抽象与封装 数据抽象 数据封装 数据抽象与封装是面向对象程序设计的基础。 数据的使用者只需要知道对数据所能实施的操作以及这些操作之间的关系,而不必知道数据的具体表示。 数据封装 把数据表示及其操作作为一个整体来进行实现。 数据的具体表示对使用者是不可见的,对数据的访问只能通过封装体所提供的对外接口(操作)来完成。 数据抽象与封装是面向对象程序设计的基础。

例:“栈”数据的表示与操作 栈是一种由若干个具有线性次序关系的元素所构成的复合数据。对栈只能实施两种操作: 进栈(push):往栈中增加一个元素 退栈(pop):从栈中删除一个元素 上述两个操作必须在栈的同一端(称为栈顶,top)进行。后进先出(Last In First Out,简称LIFO)是栈的一个重要性质。 push(...); ...pop(...); ... ;push(x);pop(y); x == y top 栈:

栈的应用 函数调用过程中用栈保存函数的局部变量、参数、返回值以及返回地址。 把表达式的“中缀”表示转换成“后缀”表示需要用到栈: 中缀:a+b/c-d 后缀:abc/+d- ......

“栈”数据的表示与操作 --非数据抽象和封装途径 定义栈数据类型 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.top++; st.buffer[st.top] = 12;

通过过程抽象操作栈数据 bool push(Stack &s, int i) { if (s.top == STACK_SIZE-1) { cout << “Stack is overflow.\n”; return false; } else { s.top++; s.buffer[s.top] = i; return true; bool pop(Stack &s, int &i) { if (s.top == -1) { cout <<“Stack is empty.\n”; { i = s.buffer[s.top]; s.top--; void init(Stack &s) { s.top = -1; }

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;

“栈”数据的表示与操作 --数据抽象和封装途径 定义栈数据类型 const int STACK_SIZE=100; class Stack { public: Stack() { top = -1; } bool push(int i); bool pop(int &i); private: int top; int buffer[STACK_SIZE]; };

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

使用栈类型数据 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() { top = NULL; } bool push(int i); bool pop(int &i); private: struct Node { int content; Node *next; } *top; }; top a1 a2 an NULL

栈类型数据的实现变化了,但对使用者没有影响! Stack st; int x; st.push(12); st.pop(x); bool Stack::push(int i) { Node *p=new Node; if (p == NULL) { cout << "Stack is overflow.\n"; return false; } else { p->content = i; p->next = top; top = p; return true; bool Stack::pop(int &i) { if (top == NULL) { cout << "Stack is empty.\n"; { Node *p=top; top = top->next; i = p->content; delete p; 栈类型数据的实现变化了,但对使用者没有影响! Stack st; int x; st.push(12); st.pop(x);

类 对象是数据及其操作的封装体,对象的特征则由相应的类来描述。 在C++中,类是一种用户自定义类型,定义形式如下: class <类名> { <成员描述> } ; 其中,类的成员包括: 数据成员 成员函数

例:一个日期类的定义 class Date { public: void set(int y, int m, int d) //成员函数 { year = y; month = m; day = d; } bool is_leap_year() //成员函数 { return (year%4 == 0 && year%100 != 0) || (year%400==0); void print() //成员函数 { cout << year << "." << month << "." <<day; private: int year,month,day; //数据成员 };

数据成员 数据成员指类的对象所包含的数据,它们可以是常量和变量。例如: class Date //类定义 { ...... private: { ...... private: int year,month,day; //数据成员说明 };

成员函数 成员函数描述了对类定义中的数据成员所能实施的操作。 成员函数的定义可以放在类定义中,例如: class A { ... void f() {...} //建议编译器按内联函数处理。 }; 成员函数的定义也可以放在类定义外,例如: void f(); //声明 void A::f() { ... } //需要用类名受限,区别于全局函数。

成员函数名是可以重载的(析构函数除外),它遵循一般函数名的重载规则。例如: class A { ...... public: void f(); int f(int i); double f(double d); ...... };

类成员的访问控制 在C++的类定义中,可以用访问控制修饰符public,private或protected来描述对类成员的访问限制。默认访问控制是private。 例如: class A { int m; //默认为private,只能在本类和友元的代码中访问。 public: //访问不受限制。 void f(); private: //只能在本类和友元的代码中访问。 int x,y; void g(); protected: //只能在本类、派生类和友元的代码中访问。 int z; void h(); };

一般来说,类的数据成员和在类的内部使用的成员函数应该指定为private,只有提供给外界使用的成员函数才指定为public。 具有public访问控制的成员构成了类与外界的一种接口(interface)。操作一个类的对象时,只能通过访问对象类中的public成员来实现。 protected类成员访问控制具有特殊的作用(继承,在派生类中使用)。

对 象 类属于类型范畴的程序实体,它一般存在于静态的程序(运行前的程序)中。 而对象则存在于动态的程序(运行中的程序)中。 对 象 类属于类型范畴的程序实体,它一般存在于静态的程序(运行前的程序)中。 而对象则存在于动态的程序(运行中的程序)中。 对象在程序运行时创建。 程序的执行是通过对象之间相互发送消息来实现的。 当对象接收到一条消息后,它将调用对象类中定义的某个成员函数来处理这条消息。

对象的创建和标识 直接方式 通过在程序中定义一个类型为类的变量来实现的,其格式与普通变量的定义相同。例如: class A { public: void f(); void g(); private: int x,y; } ...... A a1; //创建一个A类的对象。 A a2[100]; //创建100个A类对象。 分为:全局对象、局部对象和成员对象。 对象通过对象名来标识和访问。

间接方式(动态对象) 在程序运行时刻,通过new操作来创建对象,用delete操作来撤消(使之消亡)。 动态对象通过指针来标识和访问。 单个动态对象的创建与撤消 A *p; p = new A; // 创建一个A类的动态对象。 … *p … //通过p访问动态对象 delete p; // 撤消p所指向的动态对象。 动态对象数组的创建与撤消 A *q; q = new A[100]; //创建一个动态对象数组。 ...q[i]... //或者,*(q+i),通过q访问动态对象数组 delete []q; //撤消q所指向的动态对象数组。

对象的操作 对于创建的一个对象,需要通过调用对象类中定义的某个public成员函数来操作。例如: class A { int x; void f() { ... x ... }; }; int main() { A a; //创建A类的一个局部对象a。 a.f(); //调用A类的成员函数f对对象a进行操作。 A *p=new A; //创建A类的一个动态对象,p指向之。 p->f(); //调用A类的成员函数f对p所指向的对象进行操作。 delete p; return 0; }

通过对象来访问类的成员时要受到类成员访问控制的限制,例如: void func() { A a; a.f(); //OK a.x = 1; //Error a.g(); //Error a.y = 1; //Error a.h(); //Error ...... } class A { public: void f() { ...... //允许访问:x,y,f,g,h } private: int x; void g() protected: int y; void h() A a; ...//能访问a.x、a.y、a.g和a.h吗? }; 1、成员函数在类外定义,也是本类的代码!

对象的初始化 当一个对象被创建时,它将获得一块存储空间,该存储空间用于存储对象的数据成员。 在使用对象前,需要对对象存储空间中的数据成员进行初始化。 C++提供了一种对象初始化的机制:构造函数。 构造函数是类的特殊成员函数,它的名字与类名相同、无返回值类型。创建对象时,构造函数会自动被调用。例如: class A { int x,y; public: A() { x = 0; y = 0; } //构造函数 ...... }; A a; //创建对象a:为a分配内存空间,然后调用a的构造函数A()。 a x: y:

构造函数可以重载,其中,不带参数的(或所有参数都有默认值的)构造函数被称为默认构造函数。例如: class A { int x,y; public: A() //默认构造函数 { x = y = 0; } A(int x1) { x = x1; y = 0; A(int x1,int y1) { x = x1; y = y1; ...... };

在创建对象时,可以显式地指定调用对象类的某个构造函数。如果没有指定调用何种构造函数,则调用默认构造函数初始化。 class A { ...... public: A(); A(int i); A(char *p); }; ...... A a1; //调用默认构造函数。 A a2(1); //调用A(int i)。 A a3("abcd"); //调A(char *)。

对象消亡前的处理 在类中可以定义一个特殊的成员函数:析构函数,它的名字为“~<类名>”,没有返回类型、不带参数、不能被重载。例如: class A { ....... public: A(); //构造函数 ~A(); //析构函数 }; 一个对象消亡时,系统在收回它的内存空间之前,将会自动调用析构函数。 可以在析构函数中完成对象被删除前的一些清理工作(如:归还对象额外申请的资源等)。

class String { int len; char *str; public: String(char* s) { len = strlen(s); str = new char[len+1]; //申请资源 strcpy(str, s); } ~String() { delete[] str; //归还资源 str = NULL; }; void f() { String s1("abcd"); //调用s1的构造函数 ...... } //调用s1的析构函数 注意:系统为对象s1分配的内存空间只包含len和str(指针)本身所需的空间,str所指向的空间不由系统分配,而是由对象作为资源自己处理! s1 len: str: 4 abcd

class Stack { public: //对外的接口 Stack() { top = NULL; } ~Stack() { while (top != NULL) { Node *p=top; top = top->next; delete p; } bool push(int i); bool pop(int &i); private: struct Node { int content; Node *next; } *top; };