版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1第五章
类与对象的深入介绍参见教材5.4、5.5、5.6、5.7节2主要内容及要求主要内容类与对象概述构造函数和析构函数静态成员和常量成员友元要求重点掌握类、对象、构造函数、析构函数、静态成员、常量成员了解友元的使用
3设计实例以一个例子进一步比较面向对象程序设计思想与非面向对象程序设计思想,展示从不使用函数到引入函数进行模块化编程,再到引入类进行面向对象编程的程序设计思路4[例]:桌球城计费问题问题描述:某桌球城营业时间为9:00到23:00,每张桌子收费标准为9:00到18:00或21:00至23:00为0.45元/分钟、18:00至21:00为0.60元/分钟。编写C++程序,输入顾客占用一张桌子的起、止时间(精确到分钟),输出计费结果。
5非模块化设计
按照自顶向下分解的思路,首先可将此问题分解成几个小问题:输入起止时间根据起止时间计算费用输出费用6非模块化设计(续)输出费用使用简单的输出语句即可输入起止时间,也可使用简单的输入语句,输入两个整数分别代表小时和分钟为使程序能应付所有情况,要检查用户输入的小时和分钟是否正确,检查包括输入的小时应大于等于9小于等于23,且如果等于23时分钟应该为0输入的分钟应大于等于0小于等于59输入的起始时间应小于(先于)终止时间
7非模块化设计(续)进一步考虑如何根据起止时间进行计费显然,因为不同时间段的收费标准不同,需要分时间段计算费用可以根据起止时间求出每个时间段的分钟数,然后使用该分钟数乘相应的费用则可得到总费用因为只有从18点到21点的费用不同,因此也可先求出总的分钟数乘一般费用(即0.45元),然后再求用户在18点到21点这个范围内占用了多少分钟,用它乘超出费用(即0.15元),两个费用相加即得到总费用8非模块化设计的源代码//程序:CALFEE1.CPP//功能:为某桌球城计算费用(第一个版本,所有功能都用主函数main()实现)#include<iostream.h>
intmain(){intstart_hour,start_minute; //输入的起始时间
inttemp_s_hour,temp_s_minute;//用来计算附加时间的临时起始时间
intend_hour,end_minute; //输入的终止时间
inttemp_e_hour,temp_e_minute;//用来计算附加时间的临时终止时间
inttotal_minute; //从起始时间到终止时间总的分钟数
intextra_minute; //在18点到21点时间段要计算附加费用的分钟数
floattotal_fee; //总的费用9非模块化设计的源代码(续)while(1){ //可多次输入终止和起始时间,多次计算费用
//输入两个整数分别代表小时与分钟,中间用空格分开
cout<<"Pleaseinputthestarttime(suchas1220):"; cin>>start_hour>>start_minute;
//如果用户的小时与分钟都为零,则结束程序
if(start_hour==0&&start_minute==0)return0; cout<<"Pleaseinputtheendtime:"; cin>>end_hour>>end_minute;
10非模块化设计的源代码(续)
//检查输入的起始时间和终止时间是否合法
if(start_hour<9||start_hour>=23){//起始的小时必须在9到22之间
cout<<"Thestarthouriserror!\n"; continue;}if(start_minute<0||start_minute>59){ //分钟必须在0到59之间
cout<<"Thestartminuteiserror!\n"; continue;}if(end_hour<9||end_hour>23){ //终止的小时必须在9到23之间
cout<<"Theendhouriserror!\n"; continue;}
11非模块化设计的源代码(续)//如果终止小时为23,则分钟只能为0if(end_hour==23&&end_minute>0){ cout<<"Theendtimeiserror!\n"; continue;}//分钟必须在0到59之间if(end_minute<0||end_minute>59){ cout<<"Theendminuteiserror!\n"; continue;}
12非模块化设计的源代码(续)
//计算总的时间(分钟数)
total_minute=(end_hour-start_hour)*60+end_minute-start_minute;
//如果总的分钟数小于0,则表示输入的起始时间大于终止时间
if(total_minute<0){ cout<<"Thestarttimeislaterthantheendtime!\n"; continue;}
13非模块化设计的源代码(续)//计算输入的起始时间和终止时间落在18点到21点时间段的分钟数,//这段时间要计附加费用
if(end_hour<18||start_hour>=21) extra_minute=0;else{ if(start_hour>=18){
//如果起始小时在18点以后,
//则落在18点到21点之间的起始小时就是输入的起始小时
temp_s_hour=start_hour;temp_s_minute=start_minute; }else{//否则(输入的起始小时在18点以前)从18点算起
if(end_hour>=18) temp_s_hour=18;temp_s_minute=0;} 14非模块化设计的源代码(续)if(end_hour<21){
//同样如果终止小时在21点以前,
//则落在18点到21点之间的终止小时就是输入的终止小时
temp_e_hour=end_hour;temp_e_minute=end_minute;}else{
//否则(输入的终止小时在21点以后)只能算到21点
temp_e_hour=21;temp_e_minute=0;}extra_minute=(temp_e_hour-temp_s_hour)*60+temp_e_minute–temp_s_minute;} 15非模块化设计的源代码(续)
//计算总费用
total_fee=total_minute*0.45+extra_minute*0.15;
//输出总费用
cout<<"From"<<start_hour<<":"<<start_minute; cout<<"to"<<end_hour<<":"<<end_minute<<","; cout<<"Thetotalfeeis:"<<total_fee<<"\n";}}16非模块化设计的不足及改进上述解决方案没有进行模块化设计,所以程序重用的可能性很小,要扩充也不容易为提高程序的可重用性,可将其中容易被重用的功能分离出来,形成独立的模块上述程序的核心功能是根据不同时间段的计费标准求每个起止时间的费用。而上述程序还有输入合法性检查、计算两个时间之间的分钟数等与核心功能关系不密切的一些功能,这些功能被重用的机会比较大17非模块化设计的不足及改进(续)进一步思考程序哪些地方有需要扩充的可能性改变时间段(可能性较大)根据需要增加一些收不同费用的时间段改变时间段的费用…将时间的计算精确到秒(可能性较小)18使用函数作为模块的设计
根据上述分析,可设计如下一些函数:
//ROUTINE:check_time //FUNCTION:检查时间是否合法
//PARAMETER:hour小时,minute分钟
//RETURN:如果合法则返回1,否则返回0
intcheck_time(inthour,intminute);
注:该函数被重用的机会较大,为保证其可重用性,该函数应该只检查具有通用性的合法性,即该函数只检查小时hour是否在0到24之间。这里将合法性检查分成两部分,把与本问题密切相关的合法性检查(检查hour是否在9到23之间)放在上层程序中进行。19使用函数作为模块的设计
(续)//ROUTINE:calculate_minutes//FUNCTION:计算两个时间之间的分钟数//PARAMETER:start_hour,start_minute起始时间//(小时和分钟)// end_hour,end_minute终止时间(小时和分钟)//REQUIRE:起始时间和终止时间都合法//RETURN:返回两个时间之间的分钟数,若起始时间大于终止时间则返回负数
intcalculate_minutes(intstart_hour,intstart_minute,intend_hour,intend_minute);
注:该函数被重用的机会较大20使用函数作为模块的设计
(续)//ROUTINE:get_minutes//FUNCTION:计算某个起止时间(由start_hour,start_minute//end_hour,end_minute确定)在某个时间段(由min_hour,//min_minute,max_hour,max_minute确定)中的分钟数//PARAMETER:min_hour,min_minute时间段的最小时间(起始时间)// max_hour,max_minute时间段的最大时间(终止时间)// start_hour,start_minute待计算的起始时间// end_hour,end_minute待计算的终止时间
//REQUIRE:所有的时间都合法,且所有终止时间都大于相应的起始时间//RETURN:返回计算出来的分钟数。intget_minutes(intmin_hour,intmin_minute,intmax_hour,intmax_minute,intstart_hour,intstart_minute,intend_hour,intend_minute);21//程序:CALFEE2.CPP//功能:为某桌球城计算费用(第二个版本,引入函数来实现所需的功能)#include<iostream.h>intcheck_time(inthour,intminute){ if(hour<0||hour>24)return0; if(hour==24&&minute!=0)return0; if(minute<0||minute>59)return0; return1;}intcalculate_minutes(intstart_hour,intstart_minute,intend_hour,intend_minute){ return(end_hour-start_hour)*60+end_minute-start_minute;}C++codes22intget_minutes(intmin_hour,intmin_minute,intmax_hour,intmax_minute, intstart_hour,intstart_minute,intend_hour,intend_minute){inttemp_s_hour,temp_s_minute;inttemp_e_hour,temp_e_minute;
//如果起始时间在时间段的终止时间之外(即没有落在时间段中)返回0if(calculate_minutes(start_hour,start_minute,max_hour,max_minute)<=0) return0;
//如果终止时间在时间段的起始之前(同样没有落在时间段中)返回0if(calculate_minutes(min_hour,min_minute,end_hour,end_minute)<=0) return0;
//如果起始时间在时间段的起始时间之前,则从时间段的起始时间算起
if(calculate_minutes(start_hour,start_minute,min_hour,min_minute)>=0){ temp_s_hour=min_hour;temp_s_minute=min_minute;}else{
temp_s_hour=start_hour;temp_s_minute=start_minute;}minmaxstartend23
//如果终止时间在时间段的终止时间之后,则只算到时间段的终止时间
if(calculate_minutes(max_hour,max_minute,end_hour,end_minute)>=0){ temp_e_hour=max_hour;temp_e_minute=max_minute; }else{ temp_e_hour=end_hour;temp_e_minute=end_minute; }
returncalculate_minutes(temp_s_hour,temp_s_minute,temp_e_hour,temp_e_minute);}C++codes24intmain(){ intstart_hour,start_minute; intend_hour,end_minute; inttotal_minute; intextra_minute; floattotal_fee;
while(1){ cout<<"Pleaseinputthestarttime(suchas1220):"; cin>>start_hour>>start_minute; if(start_hour==0&&start_minute==0)return0; cout<<"Pleaseinputtheendtime:"; cin>>end_hour>>end_minute; if(check_time(start_hour,start_minute)==0){ cout<<"Thestarttimeiserror!\n"; continue; }
//额外的检查(与本问题密切相关的检查)
if(start_hour<9||start_hour>=23){ cout<<"Thestarthouriserror!\n"; continue; } 25 if(check_time(end_hour,end_minute)==0){ cout<<"Theendtimeiserror!\n"; continue; }
//额外的检查(与本问题密切相关的检查)
if(end_hour<9||end_hour>23){ cout<<"Theendhouriserror!\n"; continue; } if(end_hour==23&&end_minute>0){ cout<<"Theendtimeiserror!\n"; continue; } C++codes26 total_minute=calculate_minutes(start_hour,start_minute,end_hour,end_minute);
if(total_minute<0){ cout<<"Thestarttimeislaterthantheendtime!\n"; continue; }
//计算输入的起止时间落在时间段18点到21点之间的分钟数
extra_minute=get_minutes(18,0,21,0,start_hour,start_minute,end_hour,end_minute); total_fee=total_minute*0.45+extra_minute*0.15; cout<<"From"<<start_hour<<":"<<start_minute; cout<<"to"<<end_hour<<":"<<end_minute<<","; cout<<"Thetotalfeeis:"<<total_fee<<"\n";}}C++codes27使用函数作为模块的设计
(续)引入函数后,整个程序似乎更复杂了,但实际上主函数相对简单了在理解整个程序时,可先理解主函数,理解主函数时并不需要关心它调用的函数的实现细节,只需要知道它调用的每个函数的功能即可;在理解主函数后,再研究每个函数的实现细节总体而言,该程序比上一个程序更好理解,可重用性和可扩充性也更好
28使用函数模块的不足之处函数的参数可能过于复杂(?)单个函数功能过于简单(?)代码复用(?):你觉得怎样?29使用类作为模块的设计根据问题的特征,容易想到使用时间类TIME,根据要求解的问题可初步设计该类的界面为:
classTIME{public:
//ROUTINE:TIME //FUNCTION:构造函数,设置类中数据成员的初始值
//PARAMETER:hour,minute待设置时间的小时和分钟,//如果其值不是合法的时间,则将小时和分钟都置为0. //ENSURE:保证所设置的小时和分钟是合法的
TIME(inthour=0,intminute=0);30使用类作为模块的设计(续)
//ROUTINE:set_time//FUNCTION:因为使用构造函数设置小时和分钟不能
//报告传入的参数是否合法,所以提供本函数设置小
//时和分钟,以便报告参数的合法性。
//PARAMETER:hour,minute待设置时间的小时和分钟。
//ENSURE:保证所设置的小时和分钟是合法的
//RETURN:如果传入的小时和分钟是合法的时间
//则返回1,否则返回0 intset_time(inthour,intminute);31使用类作为模块的设计(续)
//ROUTINE:calculate_minutes //FUNCTION:计算从起始时间start到当前对象
//所代表时间之间的分钟数
//PARAMETER:start起始时间
//RETURN:两个时间之间的分钟数,若起始时
//间大于终止时间则返回负数
intcalculate_minutes(constTIME&start);32使用类作为模块的设计(续) private:
//ROUTINE:check_time //FUNCTION:检查时间是否合法,用户使用的TIME类对象
//都要保证是合法时间,因此将检查时间是否合法的功能设
//计为私有,只在TIME类自己的函数set_value()中调用,
//保证所设置的时间合法。上层程序可通过检查是否设置
//时间成功而进行判断。
//PARAMETER:hour小时,minute分钟
//RETURN:如果合法则返回1,否则返回0 intcheck_time(inthour,intminute); inthour,minute; //当前对象所代表的小时和分钟。};33使用类作为模块的设计(续)计算某个起止时间在某个时间段中分钟数的函数get_minutes()很难看成是某个“时间”的行为,而应作为“时间段”这样的对象上的操作。因此不将它设计为时间类TIME的成员函数,仍使用游离函数实现这项功能,该函数的界面如下:
//ROUTINE:get_minutes//FUNCTION:计算某个起止时间(由start和end确定)在某个时
//间段(由min和max确定)中的分钟数
//PARAMETER:min时间段的最小时间、max时间段的最大时间
//start待计算的起始时间、end待计算的终止时间
//REQUIRE:所有终止时间都大于相应的起始时间
//RETURN:返回计算出来的分钟数。
intget_minutes(constTIME&min,constTIME&max,constTIME&start,constTIME&end);34使用类作为模块的设计(续)为支持判断一个时间是否在另一时间之后,在类TIME中增加如下成员函数:
//ROUTINE:is_later_than//FUNCTION:判断当前对象所代表的时间是否
//在时间start之后。
//PARAMETER:start所比较的起始时间
//RETURN:如果当前对象所代表的时间在时间
//start之后返回1,否则返回0.intis_later_than(constTIME&start);35使用类作为模块的设计(续)为了检查时间是否在某个特定的范围内,可将时间应满足的范围也作为类TIME的属性之一,因此为类TIME增加如下数据成员:
intmin_hour,min_minute; //时间范围的最小时间
intmax_hour,max_minute; //时间范围的最大时间36使用类作为模块的设计(续)同时要增加成员函数,设置时间应满足的范围:
//ROUTINE:set_range//FUNCTION:设置时间应该满足的范围
//PARAMETER:min_hour,min_minute时间范围的最小时间
// max_hour,max_minute时间范围的最大时间
//RETURN:如果设置成功则返回1,否则返回0.intset_range(intmin_hour,intmin_minute,intmax_hour,intmax_minute);在设置时间范围时应检查所设置的范围是否正确,包括所传入的最小时间和最大时间是否合法所传入的最小时间是否小于最大时间时间范围是否在0点到24点之间…37//程序:CALFEE3.H//功能:为某桌球城计算费用(第三个版本,引入类来实现所需的功能)classTIME{public:TIME(int=0,int=0);
intset_range(intmin_hour,intmin_minute,intmax_hour,intmax_minute);intset_time(inthour,intminute);intget_hour()const;intget_minutes()const;intcalculate_minutes(constTIME&start)const;intis_later_than(constTIME&start)const;private:intcheck(inthour,intminute)const;intcalculate(intstart_hour,intstart_minute,intend_hour,intend_minute)const;inthour,minute;intmax_hour,max_minute;intmin_hour,min_minute;};C++codes38//程序:CALFEE3.CPP//功能:为某桌球城计算费用(第三个版本,引入类来实现所需的功能)#include<iostream.h>#include“CALFEE3.H”TIME::TIME(inthour,intminute){ set_range(0,0,24,0); if(check(hour,minute)==0){ hour=0;minute=0; } TIME::hour=hour;TIME::minute=minute;} C++codes39intTIME::
set_range(intmin_hour,intmin_minute,intmax_hour,intmax_minute){if(calculate(0,0,min_hour,min_minute)<0)return0;if(calculate(0,0,max_hour,max_minute)<0)return0;if(calculate(min_hour,min_minute,24,0)<0)return0;if(calculate(max_hour,max_minute,24,0)<0)return0;if(min_minute<0||min_minute>59)return0;if(max_minute<0||max_minute>59)return0;if(calculate(min_hour,min_minute,max_hour,max_minute)<=0) return0;TIME::min_hour=min_hour;TIME::min_minute=min_minute;TIME::max_hour=max_hour;TIME::max_minute=max_minute;return1;}C++codes40 intTIME::set_time(inthour,intminute) { if(check(hour,minute)==0)return0; TIME::hour=hour;TIME::minute=minute; return1; } intTIME::get_hour()const { returnhour; } intTIME::get_minutes()const { returnminute; }C++codes41intTIME::calculate_minutes(constTIME&start)const{returncalculate(start.hour,start.minute,hour,minute);}
intTIME::is_later_than(constTIME&start)const{if(calculate(start.hour,start.minute,hour,minute)>=0)return1;elsereturn0;}intTIME::
check(inthour,intminute)const{ if(minute<0||minute>59)return0; if(calculate(min_hour,min_minute,hour,minute)<0)return0; if(calculate(hour,minute,max_hour,max_minute)<0)return0; return1;} C++codes42intTIME::calculate(intstart_hour,intstart_minute,intend_hour,intend_minute)const{ return(end_hour-start_hour)*60+end_minute-start_minute;}
C++codes43intget_minutes(constTIME&min,constTIME&max,constTIME&start,constTIME&end){ TIMEtemp_start,temp_end; if(start.is_later_than(max))return0; if(min.is_later_than(end))return0; if(start.is_later_than(min))temp_start=start; elsetemp_start=min; if(end.is_later_than(max))temp_end=max; elsetemp_end=end; returntemp_end.calculate_minutes(temp_start);}C++codes44#include“CALFEE3.H”intmain(){ inthour,minute; TIMEstart,end; TIMEmin(18,0),max(21,0); inttotal_minute; intextra_minute; floattotal_fee; start.set_range(9,0,23,0); end.set_range(9,0,23,0); while(1){ //输入起止时间
cout<<"Pleaseinputthestarttime(suchas1220):"; cin>>hour>>minute; C++codes45 if(hour==0&&minute==0)return0; if(start.set_time(hour,minute)==0){ cout<<"Thestarttimeiserror!\n"; continue; } cout<<"Pleaseinputtheendtime:"; cin>>hour>>minute; if(end.set_time(hour,minute)==0){ cout<<"Theendtimeiserror!\n"; continue; } total_minute=end.calculate_minutes(start); C++codes46 if(total_minute<0){ cout<<"Thestarttimeislaterthantheendtime!\n"; continue; } extra_minute=get_minutes(min,max,start,end); total_fee=total_minute*0.45+extra_minute*0.15; cout<<"From"<<start.get_hour()<<":"<<start.get_minutes(); cout<<"to"<<end.get_hour()<<":“<<end.get_minutes()<<","; cout<<"Thetotalfeeis:"<<total_fee<<"\n";}}C++codes47小结非模块化设计->使用函数作为模块->使用类作为模块。得到的程序似乎越来越复杂,但其结构却是越来越清晰、越来越容易理解这种可理解性是指在不同层次的理解,通过引入函数,特别是引入类,将程序结构划分成不同层次,每个层次上都更好理解,整个程序的结构也更为清晰使用面向对象思想设计的程序具有更强的重用性和扩充性
48关于面向对象程序设计的若干基本问题面向对象程序设计是一种理念(idea):思维和方法论的问题。是某种语言里面支持面对对象的具体机制:程序语言的运用问题
----(类和对象、继承、多态性和模板等)。49抽象生活在这个复杂的世界中,我们经常会借助“抽象”来理解或解决很多事物。
50抽象数据抽象:只关心该数据“是什么”,而不关心它是如何运作的。行为抽象:只关心这个行为能够为我们带来什么,而不关心这个行为的具体实现方法。51抽象在软件设计中,抽象早已证明是开发和管理大型复杂项目的绝对“必需品”。例如下面的计算就是用到了抽象:
y=sin(x)+sqrt(x)
;函数调用就是一种行为抽象:我们只关心个函数的功能,所以在决定是否使用它们以及使用的时候都只依赖于这函数的接口,整个过程中无需知晓它们内部的具体实现。52抽象用户:抽象是用户的“权利”。设计者:必须关注内部实现。被抽象的对象本身:必须具备说明部分用以向用户说明自身,以及具备具体的实现部分。53抽象数据类型(AbstractDataType,ADT)在程序设计中,对于被抽象的数据,称为抽象数据类型(AbstractDataType,ADT)。一种ADT应具有(1)说明部分(说明该ADT是什么):说明部分描述数据值的特性和作用于这些数据之上的操作。ADT的用户仅须明白这些说明,而无须知晓其内部实现。(2)实现部分。54抽象数据类型时间类型类型时间数据每个日期的值都是时、分操作设置时间获取时间设置时间段比较两时间为了实现一个ADT,应该:1)为这种ADT选择具体的
数据表达;2)实现每种操作。inthourminute;set()gethour()Getminute()setRange()isLaterThan()55数据封装与隐藏时、分这些内部数据被隐藏在模块中(这里的模块是文件TIME.cpp),客户程序只能通过该模块提供的公开操作来访问这些数据,而不能直接访问这些数据。这种保护措施称为信息隐藏(InformationHiding)把这这些数据与相关操作组织在一起的方式称为封装(Encapsulation)。56数据封装与隐藏客户程序:只关心TIME能够提供那些公开的操作(即提供了哪些函数可调用),并不关心这些操作的具体实现。也就是说,用户只关心TIME能“做什么”,而不关心它内部“如何做”。封装就是实现ADT的策略,而信息隐藏则是ADT的特点。信息隐藏和封装是软件开发的必要技术,亦具商业价值。57术语ADT类对象抽象抽象具体具体实例58类与对象概述类:描述实体的抽象概念。描述一群实体的共同结构对象:对应于单个的具体实体。在程序中模拟实体。59类与对象概述类背后蕴涵的基本思想是数据抽象和封装数据抽象是一种依赖于接口和实现分离的设计和编程技术类设计者必须关心类是如何实现的类的使用者不必了解类的实现细节。相反,使用某个类的程序员仅需了解类的接口,他们可以抽象地考虑该类做什么,而不必具体地考虑该类如何工作60类与对象概述封装是一种将低级元素结合起来形成新的、高级实体的技术函数是封装的一种形式函数所执行的细节行为被封装在函数本身这个更大的实体中被封装的元素隐藏了它们的实现细节——我们可以调用一个函数但不能访问它所执行的语句类也是一个封装实体:它代表若干成员的聚集,大多数设计良好的类隐藏了实现该类的成员61为什么要使用类?作为模块,类是函数的扩充函数的引入是模块化思想在程序设计中的运用,而类的引入是模块化思想在程序设计中的进一步应用函数作为模块,功能单一且函数之间只靠参数传递数据,因此粒度过小而不能很好地贯彻模块化思想类的引入将处理同样数据的函数封装在一起,提高模块的粒度;模块之间的关系(即类之间的关系)可通过类的界面更明确地表示,从而系统的结构更为清晰;类具有比函数强的功能,重用的机会也比函数高62为什么要使用类?(续)作为类型,类是基本数据类型的扩充,通过明确类的可用操作而为类型提供更为丰富的语义类的实例(即对象)是现实世界实体的模拟,通过使用类可使得解决问题的程序空间与问题空间在结构上一致,从而整个软件系统的结构更加容易理解。63数据vs.操作数据操作操作2()操作3()操作1()数据(ADT)操作1操作2操作3数据(a)(b)64数据vs.操作数据与操作“主动与被动”的关系反映出人类思考问题、寻求解决方案的两大思路:面向过程的思考方式面向对象的思考方式思考方式不同就会导致算法结构不同,从而最终导致程序的不同。65面向过程设计也称为:功能分解(functionaldecomposition)结构化程序设计(structureddesign)模块化编程(modularprogramming)自上而下逐步求精(top-downdesign,stepwiserefinement)。66步骤1步骤2步骤D步骤E步骤F步骤G步骤H步骤a步骤b步骤3步骤4步骤B步骤C步骤5步骤6子问题I步骤A子问题II子问题III步骤I步骤II步骤III子问题1子问题2子问题3子问题6子问题C第0层第1层第2层第3层抽象具体解决这个问题面向过程设计67面向过程设计步骤1:输入日期步骤2:计算该日期是当年第几天步骤1:判断子问题:计算该日期是当年第几天cin>>yr>>mo>>day;num=Calulate(yr,mo,day);if(num...){...}intCalulate(...){...}函数:Calulate68面向过程设计在构建大型软件系统时,面向过程的设计往往导致程序有两大致命缺陷:导致程序结构不灵活。若高层算法需要修改,那么可能底层的算法也因此需要修改。导致代码难以复用。69面向对象的程序设计面向对象的程序设计(objectorienteddesign,OOD)已被证明是开发和维护大型软件的更好的设计方式,它在程序结构、代码复用、封装隐藏等方面,有着面向过程设计难以企及的优势。70面向对象的程序设计Q:什么是对象?A:一般说来,任何事物都可以看成对象。我们要考察或研究现实或思维世界中的某个实体,那么它就成为我们的对象。Q:OOD把问题视作什么?A:是把问题视作各类实体(对象)的组合。Q:它关注对象的什么?A:它关注对象中包含的数据及作用于这些数据之上的操作,也需要关注对象之间的关系和相互作用。
71类与对象概述面向对象程序设计的基本步骤:标识类与对象,即求解问题时需要设计哪些类确定类与类之间关系设计类的界面并实现,类的界面由类与类之间的关系确定
类的引入是使用C++语言进行面向对象程序设计的基础,学习C++语言,不仅要知道有关类的语法,更重要的是要掌握如何在实际编程时引入类和设计类的界面72面向对象的程序设计Q:如何确认问题中的对象?一般来说,我们需要在问题域中寻求对象,即仔细研究问题的定义,从中搜索各重要的名词和动词。名词很可能就是对象,而动词可能就是对象的操作。73面向对象的程序设计下面给出问题的一部分:“。。。。。。程序必须解决学生的图书馆帐户。允许学生通过帐户借书、还书,允许学生往帐户中存钱,逾期罚款从帐户中扣除。。。。。”重要名词:学生帐户重要动词:借书、还书、存款、扣除这个问题可能存在两类对象:学生student和帐户account。account的数据属性应该包括:借书信息、余额、每次存款、取(罚)款的时间等。操作应该包括借书BorrowBooks、还书ReturnBooks、存钱Deposit、罚款Fine。student应该有哪些数据和操作,需要这个问题的更多信息才能确定。74类有多少种?与待求解问题相关的类。这些类的对象用于模拟现实世界中的实体。程序所要实现的功能主要由这些类的对象之间进行消息传递而完成,是反映现实和思维世界中的事物或概念的对象。例如“房子”、“银行帐号”、“纳税人”、“计数器”等;与计算机有关的类。包括与用户界面有关的类、与操作系统进行交互的类、与数据库管理系统进行交互的类,等等。75面向对象的程序设计对于第[1]类的对象,要将其翻译为程序算法中的对象(即确定其数据和操作),往往会发生剧烈的变异。虽然有一些抽象概念是有确切的物质形态与之相对应,但往往我们要转化的仍是这些抽象概念。有一些对象确实存在于物质世界中,但如果要转化为程序中的对象,就要抽取我们需要的属性。运用面向对象时,人类思维的往往需要跳跃式突变,很多时候绝无面向过程中的“自然而然”。这也是初学者感到困难的地方。76面向对象的程序设计模拟现实世界或思维世界,不能奴隶式的追随我们之所见;而是需要有洞察力,用思维的力量直达对象的本质,才能确定对象及其操作。不断实践,是提高面向对象程序设计能力的唯一途径。77面向过程vs.面向对象程序函数函数函数程序对象对象对象操作数据操作数据操作数据(a)(b)78类的定义一般形式:class类名{public:
公有数据和函数
private:
私有数据和函数};79类成员的分类
从语法形式上可分为数据成员和成员函数
从访问控制角度可分为私有成员、受保护成员与公有成员
从对象存储角度可分为静态成员和非静态成员以及常量成员与非常量成员
80类作用域由声明类时所使用的“{”和“}”形成类作用域范围包括类定义中左右花括号括住的部分,以及写在类定义外面的所有成员函数的实现体在类作用域中声明的标识符只在该类中可见,且其作用域与该标识符声明的次序无关(如类中有两成员函数fun1()和fun2(),无论那个在前申明,在类的实现中均可相互调用)
81类成员的访问控制
访问控制是指在类作用域以外能否访问类的成员C++语言规定,类作用域以外只能访问类的公有成员,不能访问类的私有成员和受保护成员,不过类的受保护成员可由类的后代类在一定的继承访问控制方式下访问82类的界面与类的实现类的界面(interface)不包括类成员函数实现,只有成员函数原型的类定义包括数据成员、成员函数的原型类的实现(implementation)各成员函数的实现的总称包括成员函数的定义一般用文件将类的界面与实现分离界面文件(specificationfile):.h或.hpp实现文件(implementationfile):.cpp83//C.hclassC{
数据成员与函数成员的声明}整体框架//client.cpp#include“C.h”
intmain(){Cobj;obj.func2();...}//C.cpp#include“C.h”intC::fun1(){
函数成员的实现}intC::fun2(){...}...类C84对象的创建、使用与撤销对象(object):具有类类型的变量,又称为类的实例(instance)程序运行时,通过为对象分配存储空间来创建对象每个对象占据内存的不同区域,保存不同的数据,但操作数据的代码相同可用成员选择运算符“.”使用对象的public成员调用对象的成员函数时,成员函数代码中所引用的类成员指的是对象实例中的成员
85对象的创建、使用与撤销(续)创建对象时,系统自动调用构造函数,完成对象的初始化对象生存期由对象的声明决定(与变量类似),对象生存期结束时,撤销对象撤销对象时,系统自动调用析构函数,执行一些收尾任务(如,动态分配内存的回收)对象中各个数据成员的生存期由对象的生存期决定86构造函数函数名与类名相同,且不能指定返回值类型在创建新对象时由系统自动调用构造函数对对象进行初始化,可在其中约束初始值的合理范围
全局对象的构造函数在main()函数执行之前被调用局部静态对象的构造函数当程序第一次执行到相应的声明(定义)语句时被调用
87析构函数函数名字为在类名前加“~”不能指定返回值类型无形式参数在对象生存期结束时由系统自动调用
88//程序:CON_DESDEMO.CPP//功能:演示对象的创建与撤销(构造函数与析构函数的调用)#include<iostream.h>classDEMO_CLASS{public: DEMO_CLASS(inti); ~DEMO_CLASS();private: intvalue;};DEMO_CLASS::DEMO_CLASS(inti){ cout<<"Initialvalueis"<<i<<"\n"; value=i; return;}构造函数、析构函数例子89DEMO_CLASS::~DEMO_CLASS(){ cout<<“Destroyobject(“<<value<<")!\n"; return;}DEMO_CLASSobj1(10);//声明一个全局对象intmain(){ cout<<"Thisisthebeginningofmain()“<<endl; staticDEMO_CLASSobj2(20); //声明一个局部静态对象
DEMO_CLASSobj3(30); //声明一个局部自动对象
DEMO_CLASSobj4(40); cout<<"Thisistheendofmain().\n"; return0;}构造函数、析构函数例子90程序输出Initialvalueis10Thisisthebeginningofmain()Initialvalueis20Initialvalueis30Initialvalueis40Thisistheendofmain().Destroyobject(40)!Destroyobject(30)!Destroyobject(20)!Destroyobject(10)!注:析构函数在main函数return0后执行,单步跟踪可显示部分执行痕迹91类的静态成员(例子)classDATE{public: voidSet(intnewMonth,intnewDay,intnewYear); intgetMonth()const; intgetDay()const; intgetYear()const; voidPrint()const; voidIncrement(); voidDecrement();
staticvoidgetCount();private:intmonth;intday;intyear;
staticcount;};92对象的存储
DATEdate1,date2;//声明两个DATE的对象
yearmonthdaySet()getMonth()getDay()getYear()Print()Increment()Decrement()Set()代码getMonth()代码getDay()代码getYear()代码Print()代码Increment()代码Decrement()代码yearmonthdaySet()getMonth()getDay()getYear()Print()Increment()Decrement()对象date1对象date2类DATE共用区getCount()代码变量count93类的静态成员静态(static)成员是类的组成部分但不是任何对象的组成部分通过在成员声明前加上保留字static将成员设为static(在数据成员的类型前加保留字static声明静态数据成员;在成员函数的返回类型前加保留字static声明静态成员函数)static成员遵循正常的公有/私有访问规则。C++程序中,如果访问控制允许的话,可在类作用域外直接(不通过对象)访问静态成员(需加上类名和::)94类的静态成员(续)静态数据成员具有静态生存期,是类的所有对象共享的存储空间,是整个类的所有对象的属性,而不是某个对象的属性与非静态数据成员不同,静态数据成员不是通过构造函数进行初始化,而是必须在类定义体的外部再定义一次,且恰好一次,通常是在类的实现文件中再声明一次,而且此时不能再用static修饰95类的静态成员(续)静态成员函数不属于任何对象静态成员函数没有this指针静态成员函数不能直接访问类的非静态数据成员,只能直接访问类的静态数据成员96//date.hinterfacefunctionshere
classDATE{public: …
staticvoidgetCount();
…private:
…
staticcount;
…};//date.cppdefineandinitializestaticclassmemberintDATE::count=0;voidDATE::getCount(){ cout<<"Thereare"<<count<<"objectsnow"<<endl;}类的静态成员97类的静态成员(续)静态成员的用途:1.用来保存对象的个数。2.作为一个标记,标记一些动作是否发生,比如:文件的打开状态,打印机的使用状态等。3.存储链表的第一个或者最后一个成员的内存地址。
/pcedu/empolder/gj/c/0503/571606_1.html静态成员的用途:1.用来保存对象的个数。2.作为一个标记,标记一些动作是否发生,比如:文件的打开状态,打印机的使用状态等。3.存储链表的第一个或者最后一个成员的内存地址。
/pcedu/empolder/gj/c/0503/571606_1.html静态成员的用途:1.用来保存对象的个数。2.作为一个标记,标记一些动作是否发生,比如:文件的打开状态,打印机的使用状态等。3.存储链表的第一个或者最后一个成员的内存地址。
/pcedu/empolder/gj/c/0503/571606_1.html98初始化成员列表在以下三种情况下需要使用初始化成员列表:需要初始化引用成员数据(注:C++可以定义引用类型的成员变量);需要初始化const修饰的类成员;需要初始化的数据成员是对象的情况;99初始化引用成员数据#include<iostream>usingnamespacestd;classTest{private:int&a;public:Test(int&b):a(b)
{
}voidModify(intvalue)
{a=value;
}};intmain(){intb=3;Testtest(b);cout<<"b="<<b<<endl;test.Modify(4);cout<<"b="<<b<<endl;system("pause");return0;}100类的常量成员常量数据成员的声明与符号常量(命名常量)的声明类似常量数据成员是对象的数据成员,不能在声明时初始化,且常量一旦声明之后就不能再作为左值,所以只能在构造函数的初始化列表中对常量数据成员进行初始化常量成员函数通过在成员函数的参数表后面加保留字const声明,如果成员函数的原型与实现是分开的,那么在原型与实现两个地方都要给出保留字const常量成员函数不能修改类的数据成员(即,调用常量成员函数不会改变对象的状态),否则,将产生编译错误。常量成员函数常用于获取类的数据成员的值101类的常量成员#include<iostream>usingnamespacestd;classbase{public:constinta;int&b;public://base(intm,intn)//{a=
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024-2030年中国婴儿床市场前景规模及发展趋势分析报告
- 2024年港口起重机采购与租赁合同3篇
- 2024年塔吊租赁合同及操作培训服务3篇
- 茂名职业技术学院《刑法2》2023-2024学年第一学期期末试卷
- 2024年度物业服务合同履行监督与违约责任追究研究3篇
- 2024年标准离婚合同样本图片直接下载版B版
- 2024年版测绘服务委托书2篇
- 2024年歌手经纪公司合约3篇
- 2025年兰州货运从业资格证考试试题和答案
- 2025公对公借款合同范本
- 《物流系统规划与设计》课程教学大纲
- 护理质控分析整改措施(共5篇)
- 金属矿山安全教育课件
- 托盘演示教学课件
- 中华农耕文化及现实意义
- DB32T 4353-2022 房屋建筑和市政基础设施工程档案资料管理规程
- DBJ61-T 112-2021 高延性混凝土应用技术规程-(高清版)
- 2023年高考数学求定义域专题练习(附答案)
- 农产品品牌与营销课件
- 苏科版一年级心理健康教育第17节《生命更美好》教案(定稿)
- 车辆二级维护检测单参考模板范本
评论
0/150
提交评论