340 likes | 451 Views
第 8 章:类和数据抽象 (I). 本章目标:. 1. 理解数据封装和数据隐藏的概念. 2. 学会建立 C++ 中的类(抽象数据类型). 3. 了解类的数据成员和成员函数的属性. 4. 掌握类的数据成员和成员函数的使用. §8.1 引言. 一、面向对象的概念. 面向对象的技术有三个方面:. 面向对象分析 (OOA) [Object_Oriented Analysis]. 面向对象设计 (OOD) [Object_Oriented Design]. 面向对象程序设计 (OOP) [Object_Oriented Programming].
E N D
第8章:类和数据抽象(I) 本章目标: 1. 理解数据封装和数据隐藏的概念 2. 学会建立C++中的类(抽象数据类型) 3. 了解类的数据成员和成员函数的属性 4. 掌握类的数据成员和成员函数的使用
§8.1 引言 一、面向对象的概念 面向对象的技术有三个方面: 面向对象分析(OOA) [Object_Oriented Analysis] 面向对象设计(OOD) [Object_Oriented Design] 面向对象程序设计(OOP) [Object_Oriented Programming]
二、 对象的定义 1. 广义定义:一切事物都是对象 如:树、猫、飞机 问题:这些对象是否具有一种类在的相似? 树:有树枝,树干,树叶,…;可以净化空气,美化环境、… 猫:有毛,四只脚,…; 可以捉老鼠, … 飞机:有机翼,机身,…; 可以飞, …
2. 对象的定义: 对象是这样一种实体:它具有名字,自身状态,自身功能。 可表示成: OBJ=< ID . MS . DS . MI> 对象可受理的消息的集合 对象的数据结构 对象可受理的操作的集合 对象的标识
三、面向对象程序设计(OOP) 关键思想:模拟人类认识问题的过程 分类。 具体做法:把系统中的所有资源都看成对象,每个对象把一组数据和一组过程或函数封装在一起。 特点:具有信息隐藏(封装)的功能。
四、C/C++程序设计的风格差异 • C程序设计:以建立函数模块为主 • C++程序设计: 以建立类为主。(类中既有数据部分,也有函数部分) 类的设计是对象为核心。(从对象的角度来建立类)
§8.2 回顾C语言中的结构数据类型 1. C语言中定义的结构类型,其成员不允许有函数,例如: struct Temp{ int num; float vol; char vh; }; 结构型变量的定义: 在C中:strut Temp exp, *expt; 在C++中:1. struct Temp exp, *expt; 2. Temp exp, expt;
结构成员的访问: exp.num; expt>num; (*expt).num; 等价的 • 2. C++中的结构类型,其成员允许有函数,例如: • struct Date{ • unsigned int day; • unsigned int month; • unsigned int year; • void plan(int d, int m, int y ); • }; 若有: Date myholiday; 则: myholiday.plan(d, m, y);
例1: 给定三角形的三条边,计算三角形的面积和周长。 分析:假定三角形的边长分别为:a、b、c。取s=(a+b+c)/2,则三角形面积为: 用两种不同的实现方法来实现: (1) 结构化方法(C/C++均适用) example8_1a.cpp(p.227) (2) 在结构类型中添加成员函数的方法(C++适用) example8_1b.cpp(p.228)
讨论 • 1.三角形面积的计算形式不同; • area=area(a,b,c); area=TriAng.area(TriAng.a,TriAng.b,TriAng.c); • 成员函数的定义形式:double Shape::area(double a,double b,double c){ ……} • 3. 成员函数area()不允许单独调用。
讨论 程序存在的问题: 1.对不合法的数据没有检验,有可能导致错误的结果; 例如:如果输入了不合法的值:负数、字符(如:5,t)等,程序仍然会使用不正确的值进行计算。 2.程序可直接修改成员的值; 改进方案: 1. 增加一个函数,用来检验输入数据的合法性。 2. 采用“类”数据类型,加强数据的封装性。
§8.3 C++中的数据类型——类 在C++中,类是一种自定义的数据类型。 一、类型定义 class <类名> { public: //数据成员声明;//成员函数声明; protected: //数据成员声明;//成员函数声明; private: //数据成员声明;//成员函数声明; }; 一般形式:
例如:定义一个三角形类 class Triangle{ public: Triangle (); void setSide(float x,float y,float z); float area(); int isTriangle(); private: float a,b,c; };
几点说明 1. 类定义中的两个关键字:public、private实现了数据封装; 2. public: 提供给用户的接口。可以通过该类的对象直接引用。(其下的成员函数可以被本类中的数据成员和成员函数使用) 3. private:用来描述该类对象的属性,不允许直接访问,只有成员函数或经特殊说明的函数才可以引用它们,(其下的成员函数或数据成员只能被本类中所提供的成员函数使用)
几点说明 4. protected:主要是用于继承关系(略)。 5. public、private和protected代表访问权限。 与先后顺序无关,允许多次出现。
二、实现部分 对类中各成员函数进行定义。 一般形式: 函数返回值类型 类名:: 函数名(参数列表) { 函数语句体 } 例如,Triangle类中成员函数: Triangle:: Triangle () { a=b=c=0; }
二、实现部分 void Triangle::setSide(float x,float y,float z) { a=x;b=y;c=z; } float Triangle::area() { float s; s=(a+b+c)/2; //三角形周长的一半 return sqrt(s*(s-a)*(s-b)*(s-c)); //计算三角形面积 } int Triangle:: isTriangle() { if((a+b)>c && (b+c)>a && (a+c)>b)) //满足构成三角形的条件 return 1; else return 0; }
说明: • 与类同名的成员函数称为“构造函数”。 • 如:Triangle() • 2. 构造函数的一个重要作用:初始化类对象的数据成员。 • 构造函数的调用时机:对象生命开始的时候,由系统自动调用。 • 注:关于构造函数的性质将在后续章节(第9章)叙述。 注意: 1. 成员函数也可以在类声明中直接定义(p.231)。 2. 不建议这么做。
§8.4 类成员的访问和作用域 一、类对象的定义 <类名> <对象名> 例如: Triangle triangle; //Shape类的对象 Triangle ArrTriangle[5]; //Shape类的对象数组 Triangle *PointTriangle; //Shape类的指针对象 Triangle &RefTriangle= triangle;//Shape类对象的别名 注意:对象不能直接使用private中的成员: triangle.a 是错误的。
二、类的作用域 1. 类中的成员函数可互相直接调用。 2. 类中的数据成员在类中各成员函数间属“ 公共变量”。 3. 成员函数中的变量若有与类中的数据成员同名同类,则类中的数据成员被隐藏。
例:数据成员的作用域。 //----examp2_3.cpp #include <iostream.h> class Date{ public: Date( ) {day=31; month=12; year=2000; } void getday( ); private: int day,month,year; }; void main( ) { Date myday; myday.getday( ); } void Date::getday( ) { int year=1999; cout<<"1: "<<year<<"-"<<month<<"- "<<day<<"\n"; cout<<"2: "<<Date::year<<"-"<<month<<"-" <<day<<"\n"; } 运行结果: 1: 1999-12-31 2: 2000-12-31
三、类成员的访问 两个要点: 1. 只有本类的对象可以访问本类的公有成员. 2. 本类对象不能直接访问本类中的私有成员.
例2:对【例1】提出的问题,用类来完成三角形面积的计算。(【例1】给定三角形的三条边,计算三角形的面积和周长)例2:对【例1】提出的问题,用类来完成三角形面积的计算。(【例1】给定三角形的三条边,计算三角形的面积和周长) example8_2.cpp 分析: 1. a、b、c属private,只能通过成员函数才能访问。 2. setShape() 完成对三角形边长的设置。 3. isTriangle()判断三条边长是否能合法。 4. area()计算三角形的面积。 5. 构造函数由系统自动调用。用于初始化数据成员的值。 请注意: 1. 函数isTriangle()和area()不需要参数,直接使用类中的私有数据。 设计概念: 1. 类的对象只需要通过发送消息来完成任务。 2. 类的实现过程对用户是隐藏的。
§8.5 访问函数和工具函数的意义 访问函数和工具函数是一种称呼 意义:划分封装的程度 • 访问函数:对外接口函数(public成员函数) • 作用:设置私有数据成员值; • 读取、显示和返回私有数据成员值; • 进行各种类的操作; • 测试条件的真假等。 • 2. 工具函数:非接口函数(private成员函数) • 作用: • 支持类中其他函数的操作。
例3:实现一个销售统计问题。从键盘输入某商品每个月销售了多少件,统计该年度共销售了多少件,并计算总销售额。例3:实现一个销售统计问题。从键盘输入某商品每个月销售了多少件,统计该年度共销售了多少件,并计算总销售额。 • 分析:以销售的商品作为对象; • 对象的基本属性为:①价格;②每月的销售数量。 • 基本功能为:①输入每月的销售额; ②输出年度总销售量及总销售额; ③计算年度总销售量及总销售额。 • (因计算的功能不属于对象应具有的) • 访问函数: • void setSell(int month,double price); // ① • void printMonthSell(); // ② • 工具函数: • int totalAnnualSell(); // ③ • 程序:example8_3.cpp • 思考:将工具函数改成访问函数会有什么不同?
例4:一个关于日期的类: void Temp::getday( ) { day=1; month=1; year=1980; cout<<"1: "<<year <<"-"<<month <<"-"<<day<<"\n"; } void Temp::setday( ) { day=day+9; month=month+9; year=year+20; cout<<"2: "<<year <<"-"<<month <<"-"<<day<<"\n"; } //----examp2_1.cpp #include <iostream.h> class Temp{ public: void getday( ); void setday(); private: int day,month,year; }; void main( ) { Temp myday; //myday.day=12; 错误的语句 myday.getday( ); myday.setday( ); } 1: 1980-1-1 2: 2000-10-10 运行结果:
点评程序例4: 程序中的不良之处: 1.类名不明确: Temp和Date(日期)关联不明。 2.函数功能不明确: 函数getday()和函数setday()都可以修改私有数据成员。 设计原则: 1.类名要能反映分类的思想。 2.每个函数应具有独立的功能。
例5:修改例4的程序。 无法预料的结果 //----examp2_1a.cpp #include <iostream.h> class Date{ public: void getday( ); void setday( ); private: int day,month,year; }; void main( ) { Date myday; cout<<"1:"; myday.getday(); cout<<"2:"; myday.setday(); myday.getday(); } void Date::getday( ) { cout<<year<<"-" <<month <<"-" <<day<<"\n"; } void Date::setday( ) { day=day+9; month=month+9; year=year+20; } 运行结果: 1: 19394-5855-10459 2: 19414-5864-10468
点评程序例5: 1.好的方面: 类名比较清晰,函数功能比较明确。 2.不好的方面: 程序中出现了不可预料的结果,原因是由于对象的私有数据成员的值没有初始化。 设计原则: 1.任何对象的私有成员的值都应该进行初始化,以避免产生不确定的值。 2.get函数一般不要求带参数,set函数一般要求要带参数。
例6:改进例5的程序。 //----examp2_1b.cpp #include <iostream.h> class Date{ public: void getday(); void setday(int y,int m,int d); private: int day,month,year; }; void main( ) { Date myday; int yy=2000,mm=12,dd=31; myday.setday(yy,mm,dd); myday.getday(); yy=2001,mm=1,dd=1; myday.setday(yy,mm,dd); myday.getday(); } void Date::getday( ) { cout<<year<<"-" <<month<<"-" <<day<<"\n"; } void Date::setday(int y,int m,int d) { day=d; month=m; year=y; } 运行结果: 1980-1-1 2000-10-10
点评程序例6: 产生的新问题: 必须要先调用set函数,对私有数据成员赋值,才能确保程序的稳定。 设计原则: 1.任何对象生成之处,就拥有初始值。 2.set函数一般使用在程序中间,需要修改数据成员数值的时候。 3.采用构造函数为对象的数据成员赋初值。
保存在 1. 类的定义 <基本名>.h 保存在 2. 类中成员函数的定义 <基本名>.cpp 编译成 目标代码1 保存在 3. 主程序(头文件包含类定义文件) <文件名>.cpp 编译成 目标代码2 §8.6 接口和实现的分离 实际上就是将类的定义与类中的函数定义分别放在不同的文件中 4. 连接目标代码1和目标代码2 P.236——将程序example8_3.cpp进行分离
本章小结 重点: 1. 定义一个新的数据类型:类 2. 类中成员的性质: private、public 3. 一种属于"类"的数据类型:对象 4. 对象访问类中成员的权限 5. 访问函数和工具函数 6. 接口和实现的分离
本章练习 一、选择题 二、填空题 三、程序理解题 四、简答题 五、编程题 8.38、8.39、8.42、8.43