C++程序设计课件_第1页
C++程序设计课件_第2页
C++程序设计课件_第3页
C++程序设计课件_第4页
C++程序设计课件_第5页
已阅读5页,还剩580页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

《C++面向对象程序设计》教学内容

第1章 C++概述第2章 类和对象第3章面向对象程序设计概述第4章进一步学习类和对象第5章堆与复制构造函数第6章继承性:派生类第7章运算符重载第8章 虚函数和多态性第9章 模板第10章类库和C++的标准模板库STL第11章输入输出流第12章异常处理第2章类和对象2.1类和对象的定义2.2构造函数和析构函数2.3类与const对象和类属于同一个类的所有对象具有某些共性和相似的特征。一个类定义了一组大体上相似的对象。在面向对象的软件系统中,对象是基本的运行时实体,它既包含数据,也包括作用于这些数据的操作。对象对象的组成操作代码数据面向对象的软件系统对象4对象1对象3对象2§2.1类和对象的定义2.1.1

C++中对结构的扩展2.1.2

C++中类的定义2.1.3

C++类中的成员函数定义2.1.4

C++中对象的定义和使用2.1.5

C++中类的接口与实现2.1.6类声明与类定义2.1.7结构struct与类class的比较2.1.1

C++中对结构的扩展C++中的结构不仅可以包含不同类型的数据,而且还可以包含函数。结构中的数据和函数都是结构的成员,分别称为数据成员和函数成员。在C++中,通常把函数成员称为成员函数。C的结构体struct与C++的struct的比较C语言的结构体中数据与操作是分离的

在C++语言中将数据与操作封装在一个结构体中

structStudent{ intnumber; charname[15]; floatscore;};数据成员structStudent{intnumber;charname[15];floatscore;voiddisplay()//函数成员{ cout<<”number:”<<number; cout<<”name:”<<name; cout<<”score:”<<score<<endl;}};/*独立函数display*/voiddisplay(Student*stu){ printf(”number:%d”,stu->number); printf(”name:%s”,stu->name); printf(”score:%f\n”,stu->score);}成员函数数据成员2.1.2

C++中类的定义在C++语言中,我们通过定义新的数据类型来实现类。类既包含数据内容又包含对数据的操作,所以类是一个抽象数据类型。在C++语言中,类可被视为一种用户定义的类型。C++的结构体struct与C++的class的比较将数据与操作封装在一个C++的结构体struct中将数据与操作封装在一个C++类class中数据成员structStudent{intnumber;charname[15];floatscore;voiddisplay()//函数成员{ cout<<”number:”<<number; cout<<”name:”<<name; cout<<”score:”<<score<<endl;}};数据成员class

Student{intnumber;charname[15];floatscore;voiddisplay()//函数成员{ cout<<”number:”<<number; cout<<”name:”<<name; cout<<”score:”<<score<<endl;}};成员函数数据成员一个复数结构的例子structcomplex{doublereal;doubleimage;voidinit(doubler,doublei){real=r;image=i;}doublerealcomplex(){returnreal;}…;};成员函数数据成员私有成员和公有成员在C++中一个结构的成员通常分为两类:私有成员(private)和公有成员(

public)私有成员只能被该结构中的其他成员访问。公有成员既可以被结构内的其他成员访问,也可以被结构外的成员所访问。C++规定,在缺省情况下,结构中的成员是公有的。此处所指成员包括数据成员和成员函数。私有成员和公有成员的声明structcomplex{private:doublereal;doubleimage;public:voidinit(doubler,doublei){real=r;image=i;}doublerealcomplex(

){returnreal;}…;};私有成员公有成员类的定义class

类名{[private:]

私有数据成员和成员函数public:

公有数据成员和成员函数};//分号是不能少的//其中,class是声明类的关键字//类名是要声明的类的名字类中私有成员和公有成员的声明classcomplex{private:doublereal;doubleimage;public:voidinit(doubler,doublei){real=r;image=i;}doublerealcomplex(){returnreal;}…;};私有成员公有成员定义类时的注意事项private和public可以按任意顺序出现具有良好习惯的程序员会:把所有私有成员和公有成员归类放在一起将私有成员放在公有成员的前面private、public和protected称为访问控制符。数据成员可以是任何数据类型,但不能用auto、register或extern进行说明。只有在类对象定义之后才能给数据成员赋初值。为什么要用类代替结构?在缺省情况下,类中的成员是私有的private。类提供了缺省的安全性。2.1.3

C++类中的成员函数定义C++类中的成员函数描述了对类对象内部数据的操作方法,或者说,描述了对象的行为,因此我们又将成员函数称为方法(method)或者服务(service)。【例2.2】学生类中成员函数的定义。成员函数的定义包含在类体中classStudent{private: intnumber; charname[15]; floatscore;public: voidinit(intnumber1,char*name1,floatscore1);

voidmodify(floatscore1) { score=score1; } voidprint();};成员函数在类体外定义voidStudent::init(intnumber1,char*name1,floatscore1){作用域解析运算符

number=number1; strcpy(name,name1); score=score1;}voidStudent::print(){ cout<<”number:”<<number<<”name:”<<name<<”score:”<<score<<’\n’;}成员函数在类体内外的区别一般情况下,在类体中仅给出成员函数的原型,而把成员函数的定义放在类体之外实现。这种将类的成员函数的声明(declaration)和定义(definition)进行分离的方式有很多好处类体内定义的成员函数在编译时是以内联函数处理的,只有那些非常简短的函数才在类体中直接定义。2.1.4

C++中对象的定义和使用在C++语言中,类是用户定义的一种新类型,所以可以象声明普通变量一样来建立对象。在C++语言中,类与对象的关系就好象整型int和整型变量i之间的关系一样。注意:所有类对象都共享它们的成员函数,但是,每一个对象都建立并保持自己的数据。对象的数据成员,在C++中称为实例变量。创建对象的方法之一在定义类时同时创建对象,例如:classdate{intmonth,day,year;public:voidsetdate(int,int,int);voidprint();intgetyear();intgetmonth();intgetday();}tt;//同时创建对象tt。创建对象的方法之二在定义类后在创建对象,例如:Datadate1,date2;对象的使用对象的性质和定义要求将实例变量隐藏在对象中,对它们的访问,原则上要通过其接口——共有的成员函数来进行。访问对象的成员时,使用“.”运算符:对象名.成员名称;调用成员函数是必须指明对象,事实上,在C++语言中,我们用OOP术语发送消息来代替“调用成员函数”这个说法。2.1.5

C++中类的接口与实现一般把仅含函数原型的类声明部分称为类的接口(interface),例如:classDate{ intday,month,year;public: voidinit(intdd,intmm,intyy);//initialize voidadd_year(intn); //addnyears voidadd_month(intn);//addnmonths voidadd_day(intn); //addndays}C++中类的接口与实现(续)把类中成员函数的定义部分称为类的实现部分(implementation)。例如:voidDate::init(intdd,intmm,intyy){ day=dd; month=mm; year=yy;}inlinevoidDate::add_year(intn){ year+=n;}C++中类的接口与实现(续)C++中的类实现了接口和实现的分离,这样只要维持接口不变,实现部分可以根据需要不断改进,而不影响类的使用。类的接口部分一般存放在扩展名为h的头文件中,类的实现部分则存放在扩展名为cpp的实现文件中。在cpp文件的开头要用include将h头文件包含在其中。这个特征增强了类模块的独立性和可重用性(reuse)。设计一个类(class)时的基本原则在设计一个新的类(class)类型时的基本原则是将类的复杂的实现细节(例如用来存储该类对象的数据)和正确使用该类所需知道的要素(例如访问对象数据的函数)分离开来。这种分离的最好方式就是通过一个特定的接口来使用该类的数据结构和内部处理程序。封装(encapsulation)在进行类定义时,我们把需要提供给类的用户的在类对象上执行的操作声明为公用的(public),这样就形成了类的接口。然后按照信息隐藏(informationhiding)的原则来把类的内部数据表示和实现细节声明为私有的(private),从而完成了封装(encapsulation)。封装性及其好处所谓封装,在面向对象程序设计中的含义就是包含和隐藏对象信息,如内部数据结构和代码的能力。在C++中封装是通过类来实现的,即将内部的数据和代码声明为私有的,外界不能直接访问。封装将操作对象的内部复杂性与应用程序的其他部分隔离开来。换句话说,应用类对象的程序员不需要知道类内部的实现细节就可以使用该类。这是面向对象程序设计降低复杂性的最主要途径。2.1.6类声明与类定义类定义包含两部分:类头和类体,类体由一对花括号包围起来。一旦到了类体的结尾,即结束右边的花括号,这时就定义了一个类。一旦定义了一个类,则该类的所有成员就都是已知的,类的大小也是已知的了。类声明由于历史的原因,类的定义(classdefinition)也称为类的声明(declaration)。但是准确地说,一旦给出了类头,就声明了一个类。我们可以声明一个类但是不定义它。例如:classDate;//Date类的声明这个声明向程序引入了一个名字Date,指示Date为一个类类型。类声明(续)如果还没有定义类,那么我们就不能定义这类类型的对象但是,我们可以声明指向这种类型对象的指针或者引用。classDate; //Date类的声明Datepd,&rd;//声明了指向Date类对象的指针pd和引用rd2.1.7结构struct与类class的比较结构和类的唯一区别是:在缺省情况下结构内数据成员和成员函数是公有的public,而类中的数据成员和成员函数是私有的private。可以说C++中的结构是一种特殊的类。类(或者结构)的私有成员只能被该类(或者结构)的成员函数访问,这是C++实现封装的一种方法。§2.2构造函数和析构函数2.2.1构造函数2.2.2析构函数2.2.3重载构造函数2.2.4组合类和对象成员的初始化2.2.1构造函数constructor构造函数是类的一种特殊成员函数,用来为对象进行初始化。构造函数的函数名与类名相同。构造函数的例子1classDate{

intday,month,year;

Date(intdd,intmm,intyy);//constructor};//构造函数名与类名相同voidDate::Date(intdd,intmm,intyy){ day=dd; month=mm; year=yy;}用来为对象进行初始化。构造函数的例子2classStudent{private: intnumber; charname[15]; floatscore;public: Student(intnumber1,char*name1,floatscore1);voidmodify(floatscore1) {score=score1;} voidprint();};构造函数的声明构造函数的例子2//构造函数的定义Student::Student(intnumber1,char*name1,floatscore1){ number=number1; strcpy(name,name1); score=score1;}构造函数(续)构造函数可以有任一类型的参数,但不能具有返回类型。当定义一个对象时,系统自动调用构造函数进行初始化。构造函数与其它函数一样可以重载也可以有缺省参数。在对象生成的时候自动执行初始化,这会消除任何错误地不执行初始化的可能。这是C++面向对象程序设计帮助减少复杂性的另一途径。有缺省参数的构造函数classcomplex{private:doublereal;doubleimage;public:voidcomplex(doubler=0.0,doublei=0.0){real=r;image=i;}doublerealcomplex(){returnreal;}…;};缺省参数2.2.2析构函数析构函数的作用与构造函数正好相反,当对象被删除时,利用析构函数进行一些善后处理。一般情况下析构函数执行构造函数的逆操作,例如可以利用析构函数来释放构造函数所动态申请的内存空间。析构函数的名称与其类名称相同,并在名称的前边加~(键盘上的波浪符号)符号。一个类中只能有一个析构函数。(不能重载)析构函数不允许有返回值,并且析构函数不允许带参数。参见【例2.7】。构造函数和析构函数的例子//构造函数的定义Student::Student(intnumber1,char*name1,floatscore1){ number=number1; name=newchar[strlen(name1)+1];//申请动态内存单元

strcpy(name,name1); score=score1;}//析构函数的定义Student::~Student(){

delete[]name;//释放动态内存单元}2.2.3重载构造函数构造函数与其它函数一样可以重载也可以有缺省参数。要重载类的构造函数,只需要在类中声明构造函数的不同函数原型并且定义相关的函数体。重载构造函数主要有三个原因

1.

为了获得灵活性:提供给用户多种初始化对象的选项。2.

为了支持数组:要声明一个类的对象数组则该类应具有一个不带参数的构造函数。3.为了自定义复制构造函数,将对象进行准确的复制(拷贝):参见第5章堆与复制构造函数。

重载构造函数例一classDate{ intd,m,y;public://... Date(int,int,int);//day,month,year Date(int,int);//day,month,today’syear Date(int);//day,today’smonthandyear Date();//defaultDate:today Date(constchar*);//dateinstringrepresentation};为了获得灵活性:提供给用户多种初始化对象的选项2.2.4组合类和对象成员的初始化当一个类的对象作为一个类的成员时,称为该类的对象成员。使用对象成员着重要注意的问题是一个类的内部对象的初始化问题。类的对象成员初始化#include"iostream.h"classinner_class{ intx;public: inner_class(intz){x=z;} voidwrite(){cout<<x<<endl;}};类的对象成员初始化(续)classouter_class{ inty; inner_classx; inner_classr;public: outer_class(intz); voidwrite(){cout<<y<<endl;} voidwrite_inner_x() {x.inner_class::write();} voidwrite_inner_r() {r.inner_class::write();}};注意:outer_class类对象在通过构造函数进行初始化的同时,也要对其成员对象进行初始化。类的对象成员初始化(续)outer_class::outer_class(intz):x(20),r(-36){y=z;}main(){ outer_classobj(10); obj.write_inner_x(); obj.write_inner_r(); obj.write(); return0;}初始化参数传递链初始化和撤销的顺序先里后外:先调用对象成员的构造函数,再调用外围类的成员函数。虽然对象成员调用它们类的构造函数,但此时写的是对象的名字,而不是类名。先外后里:当撤销一个包含对象成员的对象时,该对象的析构函数将先执行,然后再调用对象成员的析构函数。【例2.9】对象成员的初始化。//注意:在定义Student类的构造函数时,必须缀上对象成员的名字birthdayStudent::Student(intnumber1,char*name1,floatscore1,intd1,intm1,inty1):birthday(d1,m1,y1)//对象成员birthday的初始化{ number=number1; strcpy(name,name1); score=score1;}2.3类与const软件工程的历史经验表明,程序内部相当多的隐蔽错误是由于无意中修改了某些数据的值造成的。对数据进行保护是减少程序中的错误,提高软件的可靠性的有效途径之一。在C++中还广泛使用const关键字,用来实现对共享数据的保护,提高软件的安全性。2.3.1常成员函数

ConstantMemberFunctionsclassDate{ intd,m,y;public: intday()const{returnd;} intmonth()const{returnm;} intyear()const; //...};使用const关键字说明的函数常成员函数不更新对象的数据成员。(只读函数)常成员函数(续)inlineintDate::year()const//正确{ returny;}inlineintDate::year()//错误{ returny;}const是函数类型的一个组成部分,因此在实现部分也要带const关键字。遗漏了const关键字常成员函数(续)inlineintDate::year()const{ returny++;}将出现编译错误信息:企图在常成员函数中修改对象的数据成员。2.3.2常对象constantobject常对象是指对象常量,定义格式如下:<类名>const<对象名>或者

const<类名><对象名>

例如:constDatecd;常对象constantobject(续)必须对常对象进行初始化,而且不能被更新。常对象只能调用它的常成员函数。普通的对象既可以调用它的常成员函数,也可以调用普通成员函数。请看下面的例子:常对象constantobject(续)voidf(Date&d,constDate&cd){ inti=d.year();//ok d.add_year(1);//ok intj=cd.year();//ok cd.add_year(1);//错误}常对象不能调用它的普通成员函数常类型cosnt

常类型的变量或对象必须进行初始化,而且不能被更新。常数组:数组元素不能被更新。类型说明符const数组名[大小]...常引用:被引用的对象不能被更新。const类型说明符&引用名常指针:指向常量的指针(1.3.2小节介绍)。C++中const关键字使用得很广泛,主要目的是对共享数据的保护,提高软件的安全性。《C++面向对象程序设计》教学内容

第1章 C++概述第2章 类和对象第3章面向对象程序设计概述第4章进一步学习类和对象第5章堆与复制构造函数第6章继承性:派生类第7章运算符重载第8章 虚函数和多态性第9章 模板第10章类库和C++的标准模板库STL第11章输入输出流第12章异常处理金尊和“计算机和软件的发展很快,新的概念名词和技术手段层出不穷,可谓日新月异。停留于表面的现象,可以让人眼花乱,应接不暇。只有深入到本质,从哲学方法论的高度上看问题,找答案,才能如庖丁解牛,游刃有余。”第3章面向对象程序设计概述3.1类与对象3.2消息和方法3.3什么是面向对象程序设计3.4数据抽象3.5封装性和信息隐藏3.6继承性与软件重用3.7多态性3.8面向对象的程序设计语言3.9面向对象的软件工程3.1类与对象3.1.1对象3.1.2类3.1.3对象与类的关系对象的广义定义什么是对象(object)?现实世界中的任何一个事物都可以看成是一个对象。自然的实体:一个人,一辆汽车,一个教师逻辑结构:一个银行帐号,一个学生的学籍档案,客户通信录对象的特性对象是人们要研究的任何事物,其特性是:1、每一个对象必须有一个名字以区别于其它对象;2、用属性(或叫状态)来描述它的某些特征;3、有一组操作,每一个操作决定对象的一种行为。//这是关于对象的广义定义面向对象的系统中的对象对象是基本的运行时实体,它既包含数据(属性),也包括作用与数据的操作(行为)。一个对象把属性和行为封装成一个整体。对象是数据和对数据的操作的结合体。从程序设计者来看,对象是一个程序模块;从用户来看,对象为他们提供了所希望的行为。类的例子人类水果类鱼类“类”是对一组具有共同属性特征和行为特征的对象的抽象。OOP中类的例子classStudent{ intnumber; char*name; floatscore;public:

Student(intnumber1,char*name1,floatscore1); ~Student(); voidmodify(floatscore1); voidprint();};属性操作什么是类(class)?在C++语言中,我们通过定义新的数据类型来构成类。在新的数据类型中,既包含数据内容又包含对数据的操作。一个类所包含的方法和数据描述一组对象的共同行为和属性。什么是类(class)?一个类定义了一个大体上相似的对象。把一组对象的共同特性加以抽象并存储在一个类中的能力,是面向对象技术最重要的一点。对象与类的关系类是对一组性质相同的对象的描述,是对一组数据和方法的封装。对象则是类的具体化,是类的实例。可以这样定义对象:对象是类的一个实例,包括了数据和过程。3.2消息和方法3.2.1消息3.2.2方法消息Message消息是要求某个对象执行其中某个功能操作的规格说明。OOP中的一条消息由消息选择器(“消息操作”或“消息名”)及若干个参数和接受消息的对象三部分组成,例如:student1.modify(score1);消息的例子接受消息的对象参数↓↓student1.modify(score1);↑

消息名发送消息与函数调用的比较1) 函数调用可以带或不带参量,但消息至少带一个参量(对象名或对象指针);它指明接受该消息的对象。消息选择器则告诉对响应作些什么。2) 消息名(消息选择器或消息操作)类似于函数名,但二者之间的本质差别在于:函数名仅代表一段可执行的代码,而消息名的具体功能的实现取决于所接受消息的对象。发送消息与函数调用的比较(续)3) 函数调用是过程式(面向过程)的即“如何做(Howtodo)”,而消息则是通知式(面向对象)的,即告诉对象“做什么(Whattodo)”,具体“如何做(Howtodo)”由对象根据接受到的消息自行确定。lst.sort();其中,lst代表一个链表对象,sort是表示排序的消息名。方法(method)“方法”对应于对象的行为(能力),即它是实现对象所具有的功能操作的代码段。在C++程序中,方法即是类中定义的成员函数,它是该类对象所能执行的操作的算法实现。通常每个类中包含多个方法(即C++的成员函数),每个方法由方法名(函数名+参数表)和说明该成员函数的算法实现的一段代码所组成。方法的例子voidStudent::print(){ cout<<”number:”<<number<<”name:”<<name<<”score:”<<score<<’\n’;}方法是与对象相连决定怎么做的操作执行代码,所以方法是实现每条消息具体功能的手段。3.3什么是面向对象程序设计3.3.1结构化程序设计方法3.3.2面向对象程序设计方法表3.1程序设计方法的发展过程1957~1972:面向过程的程序设计1968~1990:面向过程的结构化程序设计1984~今:面向对象的程序设计结构化程序设计的基本思想按功能划分模块,分而治之。分解系统时按照自顶向下的顺序,逐步求精。设计时使各模块间的关系尽可能简单,功能上相对独立,从而可单独验证模块的正确性。

结构化程序的组成

主控模块功能模块1功能模块7功能模块2功能模块3功能模块6功能模块5功能模块4图3.3结构化程序的组成结构化程序设计方法的基本特点把数据结构和处理数据的操作过程分离为相互独立的实体。用数据表达实际问题中的信息;程序代码则实现用于处理加工这些数据的算法。简而言之,就是数据和操作代码分离。

数据和操作代码分离产生的问题给程序员增加了负担:必须确保数据和代码匹配。当数据结构改变时,所有相关的处理过程都要进行相应的修改。可维护性低。即使要对不同的数据格式(结构和类型)作同样的处理计算,也必须编写不同的程序。可重用性不好。面向对象程序设计用面向对象程序设计语言中的对象和类直接模拟现实世界中的对象,将问题空间直接映射到软件空间。从而设计出尽可能直接、自然地表示问题求解方法的软件。面向对象的这种思维方式符合人类的自然思维习惯,使我们能够在程序中自然地描述现实世界的实体和问题,增强了程序代码的可读性,有利于控制软件的复杂性。模块化将一个复杂的大规模软件系统划分成几个规模较小、相对简单的模块,即分而治之。面向对象程序设计方法是按对象来划分。面向对象的软件系统由对象组成,对象之间通过消息传递互相联系,如图3.4所示。面向对象的软件系统的组成

对象1对象2对象3对象4图中箭头表示对象之间的消息传递对象作为程序模块面向对象的软件系统中每一个模块都是高度独立的对象,而对象是比功能模块粒度更大的模块。对象是由数据和对数据的操作(代码)形成的一个整体。面向对象程序设计采用封装的办法,使对象的内部实现与外界隔离,实现了信息隐藏,从而提供了更理想的模块化机制,显著地减少了程序模块间的相互干扰和依赖性。

数据抽象技术

将数据和对数据的操作封装在一起,作为一个相互依存、不可分割的整体——对象来处理,它采用数据抽象和信息隐藏技术。它将具有相同特征的对象抽象成一个新的数据类型——类,并且考虑不同对象之间的联系和对象类的重用性3.4数据抽象3.4.1什么是抽象?3.4.2数据抽象和抽象数据类型抽象性的例子classstudent{ intnumber; char*name; floatscore;public: student(intnumber1,char*name1,floatscore1); ~student(); voidmodify(floatscore1); voidprint();};抽象是有选择地忽略。抽象性(Abstraction)抽象是对复杂的现实世界的简明的表示。抽象强调了我们所关心的(感兴趣)的信息,而将我们不关心的其他信息忽略。名家之言E.Dijkstra:“设计并实现一个大规模的软件的中心问题是怎样减小复杂度,一个途径就是通过抽象”。BjarneStroustrup说:“在C++语言中,我一直在努力提高程序设计的抽象层次”。抽象数据类型一个值集和作用在值集上的操作集

C++语言中的一个类就是一个抽象数据类型每个抽象数据类型自成一个模块,模块的接口和内部实现分离开来,使用模块的应用程序员只需要知道如何使用该模块,而不必知道模块内部是如何实现的,也就是说,可以忽略模块内部的实现细节。

3.5封装性和信息隐藏所谓封装(encapsulation),就是包含和隐藏对象信息,如内部数据结构和代码的能力。在面向对象的程序中,通过创建对象将代码和数据捆绑在一起,并且在对象中包含了所有必需的代码和数据。对象是支持封装的元素。一个程序中的对象把现实世界中的实体的属性和行为封装成一个整体。信息隐藏(informationhiding)

封装的好处封装性降低了程序设计的复杂度。

封装将操作对象的内部复杂性与应用程序的其他部分隔离开来。应用程序员只需要通过对象的接口来操作对象完成特定的任务,而不需要知道对象内部复杂的细节。

封装的好处(续)提高了代码的安全性和可靠性;通过类实现封装,集中和统一了对类中数据成员的所有操作,从而可避免因分散操作造成的错误。能有效提到程序模块的独立性、可重用性和可维护性;只要类的接口(即类的共有部分)保持不变,类的结构部分的任何变化(包括数据结构和算法)都不会对使用该类的源程序有所影响,源程序可以不做任何修改。面向对象程序设计的主要特征抽象性封装性继承性多态性3.6继承性与软件重用面向对象程序设计的第二个主要特征就是继承性,继承的目的就是为了重用(reuse)。

继承,就是在一个已有的类的基础上创建一个新类

,这个新类获得了已有类的数据和操作代码,并且新类可以增加新的数据和操作代码。

继承与派生问题举例派生类的概念在已有类的基础上新增自己的特性而产生新类的过程称为派生。被继承的已有类称为基类(或父类)。派生出的新类称为派生类(或子类)。参见图3.5类的继承

继承与派生的目的继承的目的:实现代码重用。派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。C++中类的继承层次自然地表达了人们分析问题时所用的分类结构。大大改善了软件系统的可理解性和可维护性。

继承的好处(1)继承性很好地实现了代码的重用。(2)能改进软件系统的可维护性。(3)继承性使已有的程序库具有清晰的层次结构关系。3.7多态性广义多态性:自然语言中的多态性;面向对象程序设计中的多态性什么是多态性?(广义)polymorphism,“manyforms”:即多种形态在自然语言中,多态性即是“一词多义”;更准确地说,多态性是指相同的动词作用到不同类型的对象上,例如:

驾驶摩托车 驾驶汽车 驾驶飞机 驾驶轮船驾驶宇宙飞船什么是多态性?(OOP)当不同对象接受到相同的消息产生不同的动作,这种性质称为多态性。通俗地说,多态性是指用一个名字定义不同的函数,这些函数执行不同但又类似的操作,即用同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”。多态性的例子在C语言中,由于不支持多态,求绝对值的动作要求三个不同的函数名字:

abs(),labs(),fabs()分别用来求整数,长整数、浮点数的绝对值。在C++语言中,由于支持多态,求绝对值的动作可以只用一个函数名:abs()应用多态性的好处多态应用于OOP的目的是允许用一个名字来指定动作的一般类(即逻辑上相似的动作)。从而带来以下的好处:提高了处理问题的抽象级别;降低了程序设计时的复杂性;增强了软件的灵活性。面向对象程序设计的优越性重用性:有利于软件生产率的提高;符合人类的自然思维习惯,能够自然地描述现实世界的实体和问题:有利于控制软件的复杂性;更好的模块化,便于多人分工和合作开发软件;编写的代码有更好的可维护性;3.8面向对象的程序设计语言3.8.1对象程序设计语言的发展概况3.8.2几种典型的面向对象程序设计语言几种典型的OOPLSmalltalkC++JavaC#

3.9面向对象的软件工程面向对象的分析(Object-OrientedAnalysis,简称OOA)面向对象的设计(Object-OrientedDesign,简称OOD)面向对象的编程(Object-OrientedProgramming,简称OOP)面向对象的测试(Object-OrientedTest,简称OOT)面向对象的软件维护(Object-OrientedSoftwareMaintain,简称OOSM)《C++面向对象程序设计》教学内容

第1章 C++概述第2章 类和对象第3章面向对象程序设计概述第4章进一步学习类和对象第5章堆与复制构造函数第6章继承性:派生类第7章运算符重载第8章 虚函数和多态性第9章 模板第10章类库和C++的标准模板库STL第11章输入输出流第12章异常处理第4章进一步学习类和对象4.1对象数组4.2指向对象的指针4.3this指针4.4对象的赋值4.5对象作为函数参数4.6从函数返回对象4.7类的静态成员4.8类的友元4.1对象数组(Objectarrays)定义: 类名数组名[元素个数];例如:StudentaSA[10];//一个学生类对象数组通过下标访问方法: 数组名[下标].成员名;例如:aSA[j].print();对象数组初始化数组中每一个元素对象被创建时,系统都会调用构造函数类初始化该对象。通过初始化列表赋值。例:

LocationA[2]={Location(1,2),Location(3,4)};如果没有为数组元素指定显式初始值,数组元素使用缺省值初始化(调用缺省构造函数)。当数组中每一个对象被删除时,系统都要调用一次析构函数。数组元素所属类的构造函数没有自定义构造函数时,则采用缺省构造函数。各元素对象的初值要求为相同的值时,可以定义出具有缺省形参值的构造函数。各元素对象的初值要求为不同的值时,需要定义带形参(无缺省值)的构造函数。如果需要定义动态数组,需要一个无参构造函数。对象数组例子#include<iostream.h>classsamp{inta,b;public:samp(intn,intm){a=n;b=m;}intget_a(){returna;}intget_b(){returnb;}};对象数组例子(续)main(){sampob[4][2]={samp(1,2),samp(3,4),samp(5,6),samp(7,8),samp(9,10),samp(11,12),samp(13,14),samp(15,16),};inti;对象数组例子(续)

for(i=0;i<4;i++){cout<<ob[i][0].get_a();cout<<ob[i][0].get_b()<<"\n";cout<<ob[i][1].get_a();cout<<ob[i][1].get_b()<<"\n";}cout<<"\n";return0;}4.2指向对象的指针可以定义指向对象的指针,在运用对象的指针的时候,对象的成员将用箭头运算符(→)而不是点运算符(.)引用。对象的指针算法与其它数据类型的指针算法相同:它被与对象相联系的处理。例如:当对象指针增加的时候,它指向下一个对象;当对象指针减少的时候,它指向前一个对象。对象指针的用法

intmain(){

Circlec1(3),*pc;//pc为指向圆形Circle类对象的指针

pc=&c1; //将对象c1的地址赋给对象指针pc,使其指向圆形对象c1

cout<<c1.GetArea()<<endl; //通过对象名访问对象的方法

cout<<pc->GetArea()<<endl;//通过对象指针访问对象的方法

return0;}对象指针与对象数组#include"student.h"intmain(){… Student*sp=aSA; //给指针赋初值

for(inti=0;i<4;i++,sp++)sp->print();//指针加1后,指向下一个对象

sp=sp-3; //指针减3后,指针向前移动三个对象

sp->modify(87); sp->print(); return0;}4.3this指针对象是由数据和操作构成的一个整体,即使同一类的不同对象的操作也是相互独立的,各占不同的内存空间。但是,在C++实现中,这种处理方案太浪费内存空间。同类的对象能否共享该类成员函数的同一个实例呢?myBirthday.set(31,12,1997);nationalDay.set(1.10,1949);voidDate::Set(intD,intM,inty){day=D;month=M;year=Y;}那么,此时计算机怎么能区分该函数是作用在哪个对象上呢?解决方案C++在编译时,自动在每个成员函数中的第一个参数位置增加:

X*constthis;

当对象调用该函数时,编译器自动将该对象的指针作为实参传递给相应的函数,这样就可解决上述问题:voidDate::Set(Date*constthis,intD,intM,intY){this->day=D;this->month=M;this->year=Y;}

注:程序员不能将this指针的说明写在函数中C的结构体struct与C++的struct的比较C语言的结构体中数据与操作是分离的

在C++语言中将数据与操作封装在一个结构体中

structStudent{ intnumber; charname[15]; floatscore;};数据成员structStudent{intnumber;charname[15];floatscore;voiddisplay(Student*this)//函数成员{ cout<<”number:”<<this->number; cout<<”name:”<<this->name; cout<<”score:”<<this->score<<endl;}};/*独立函数display*/voiddisplay(Student*stu){ printf(”number:%d”,stu->number); printf(”name:%s”,stu->name); printf(”score:%f\n”,stu->score);}数据成员this指针this指针是由C++编译器自动产生而使用的一个隐含指针,类成员函数使用该指针来存取对象的数据成员。

定义同一类的多个对象时,每个对象拥有自己的数据成员但它们共用一份成员函数,当成员函数存取对象的数据时它们使用this指针,通过this指针指向不同对象来决定使用哪一个对象的数据成员。this指针是如何在“幕后”工作?例如complex类中的init函数:voidinit(doubler,doublei){real=r;image=i;}C++编译器在编译这个函数时自动插入this指针:voidinit(complex*this,doubler,doublei){this->real=r;this->image=i;}this指针的显式使用this指针是系统的一个内部指针,通常以隐式方式使用,但也可以为程序员所使用,例如,当运算符重载和建立链表时this指针显得十分重要。参见双向链表This指针的显式用法显式地使用this指针,还可以使C++的编译器将同名的参数与数据成员区分开来:voidDate::Set(intday,intmonth,intyear){this->day=day;this->month=month;this->year=year;}4.4对象的赋值如果两个对象属于同一种类型,那么我们可以将其中的一个对象的值(属性值)赋给另一个对象。在默认情况下,当将一个对象赋值给另一个对象时,第一个对象的数据将被按位复制到第二个对象中。对象赋值的例子【例4.4】

intmain(){Rectanglerect1(10,20),rect2(0,0);

cout<<"rect1beforeassignment:\n";rect1.show_Rect();cout<<"rect2beforeassignment:\n";rect2.show_Rect();cout<<'\n';

rect2=rect1;//将矩形对象rect1赋值给矩形对象rect2

cout<<"rect1afterassignment:\n";rect1.show_Rect();cout<<"rect2afterassignment:\n";rect2.show_Rect();

return0;}C++语言系统为Rectangle类自动提供的赋值运算符=

4.5对象作为函数参数在C++中,对象也可以象其它类型的数据一样,作为函数的参数。对象作为函数的参数时,实际参数与形式参数的传递方式也有两种:传送数值(传值调用CallbyValue)传送地址(传址调用CallbyReference)传值调用CallbyValue传值调用时,传送的是作为实际参数的对象的拷贝而不是实际对象本身。因此函数中对对象的任何修改均不影响作为实际参数的对象本身。创建形式参数对象时,是通过类的复制构造函数完成的。传值调用例子【例4.5】

classSquare{intside;public:voidset(intx){side=x;}voidout(){cout<<side<<"\n";}};voidf(Squarex)//采用传值方式将实际对象传送给临时对象x{x.out();//输出正方形Square类对象x的边长为10

x.set(100);//这仅仅修改了临时对象x的边长

x.out();//输出正方形Square类对象x的边长为100}传值调用例子(续)intmain(){Squares;

s.set(10);f(s); s.out();//输出正方形对象s的边长仍然为10,边长没有改变

return0;}对象s以传值方式传送给临时对象传址调用CallbyReference传址调用时,传递的是作为实际参数的对象的地址;在函数中对作为形式参数的对象的修改实际上就是对作为实际参数的对象的修改。传址调用时,即可以使用指针,也可以使用引用。传址调用例子【例4.6】

classSquare{intside;public:voidset(intx){side=x;}voidout(){cout<<side<<"\n";}};

voidf(Square&x)//对象的引用作为函数的参数{

x.out();//输出正方形对象x的边长为10

x.set(100);//这实际上就是对作为实际参数的对象s的修改

x.out();//输出正方形对象x的边长为100}传址调用例子(续)intmain(){Squares;

s.set(10);f(s); //传送对象s的引用给x,x和s代表同一个对象

s.out();//输出正方形对象s的边长为100,边长改变了

return0;}传送对象s的地址4.6从函数返回对象函数也可以返回对象。要从函数返回一个对象,首先必须将函数的返回类型声明为一个类类型。在函数结束之前用return语句返回该类的一个对象。

【例4.8】函数返回对象

myStringinput()//函数的返回类型为myString{charinstr[80];myStringstr;

cout<<"Enterastring:";cin>>instr;

str.set(instr);

returnstr;//返回一个myString类型的对象}【例4.8】函数返回对象(续)intmain(){myStringob;

//将返回的对象复制给对象obob=input();ob.print();

return0;}4.7类的静态成员

(staticmembers)一、静态数据成员二、静态成员函数4.7.1静态数据成员声明类的静态数据成员的方式是在变量声明的前面加static关键字。classPeople{private:

staticintcount;intage;char*name;public:

staticinttotalAge;people(intold,char*string);…

};静态数据成员的初始化#include<iostream.h>staticintPeople::count=0;staticintPeople::totalAge=0;main(){Peoplemember(32,“Smith”)’Peoplemember(18,“John”);…}注意:在用People类生成任何对象之前,必须对其中的静态数据成员单独初始化。静态成员的访问当通过对象访问静态成员时,其访问规则与一般成员的相同,即只能访问公有成员。例如:

member1.totalage=100;//正确。

member1.count=12l//错。注意:静态数据成员要在生成该类的任何实例对象之前初始化,并且不能用构造函数初始化。

静态成员变量的应用静态成员变量的最一般的作用就是对一些共享资源提供存取控制。例如,可能创建几个对象,每个对象要对某个磁盘文件进行写操作,但显然在同一时间里只允许一个对象写文件。在这种情况下,程序员可能希望说明一个静态变量指出文件何时在使用,何时处于空闲状态。然后,每个对象在写文件之前询问这个对象。请看下面的例子:静态成员变量的例子#include"iostream.h"classcl{

staticintresource;public: intget_resource(); voidfree_resource(){resource=0;}};intcl::get_resource(){ if(resource)return0;//resourcealreadyinuse else{resource=1; return1;//resourceallocatedtothisobject }}静态成员变量的例子(续)intcl::resource=0;main(){ clob1,ob2; if(ob1.get_resource()) cout<<"ob1hasresource\n"; if(!ob2.get_resource()) cout<<"ob2deniedbyresource\n"; ob1.free_resource();//letsomeoneelseuseit. if(ob2.get_resource()) cout<<"ob2canuseresourcenow.\n"; return0;}在生成该类对象之前初始化替代全局变量静态数据成员属于整个类。不论定义多少个该类的对象,静态成员只有一个拷贝。该类所有的对象都可以通过这个成员实现数据共享。通过使用静态成员变量,实际上可以完全消灭全局变量。按照软件工程的原则,应该尽可能少地使用全局变量。4.7.2静态成员函数

在类中只使用静态数据成员的函数可以定义为静态成员函数。静态成员函数的定义方法是在原函数定义前加static关键字。对静态成员函数的引用与静态数据成员一样是利用所属的类加范围运算符。静态成员函数的声明在C++中,用static把一个成员函数声明成一个静态的成员函数。例如:【例4.11】

classStudent{… public:

staticvoidinit(){

Student::count=0; Student::sum=0; }

staticfloataverage();//静态成员函数的声明};静态成员函数的声明和定义静态成员函数的主要作用floatStudent::average(){

cout<<"sumis"<<sum<<"\tcountis"<<count<<endl; cout<<"averageis"<<sum/count<<endl; returnsum/count;}//静态成员函数的定义主要用来操作静态数据成员静态成员函数的访问静态成员函数的访问通常采用如下形式:

X::静态成员函数(X是类名)注意:构造函数和析构函数不能是静态的。静态函数没有this指针,只能访问静态的数据成员,要想访问非静态成员,必须将this指针显式地传给它。

voidX::Fun(intiv,X*p){member1=iv;//错,member1是非静态成员

p->member1=iv;//正确。

member2=iv;//正确。}4.8类的友元(friends)4.8.1友元函数(朋友函数friendsfunction)4.8.2友元类4.8.1友元函数1.一个类的友元函数是在该类中说明的一个非成员函数,但允许访问该类对象的所有成员。2.将关键字friend放在函数名的前面就将该函数说明为友元函数。3.友元函数的说明可以出现在类的私有部分或公有部分。程序员应将友元看作类的接口的一部分。友元函数举例#include<iostream.h>#include<math.h>classPoint{public:Point(doublexi,doubleyi){X=xi;Y=yi;}doubleGetX(){returnX;}doubleGetY(){returnY;}

frienddoubleDistance(Point&a,Point&b);private:doubleX,Y;};虽然友员函数在类内定义,但它不属于类的成员函数,所以没有this指针。因此必须在友元函数的参数表中显示地指出要访问的对象。

友元函数举例(续)doubleDistance(Point&a,Point&b){doubledx=a.X-b.X;doubledy=a.Y-b.Y;returnsqrt(dx*dx+dy*dy);}intmain(){Pointp1(3.0,5.0),p2(4.0,6.0);doubled=Distance(p1,p2);cout<<"Thedistanceis"<<d<<endl;return0;}访问对象中的成员必须通过对象名为什么引入友元?

1. 为了提高程序的效率;友元可以直接访问对象的私有成员,因而节省调用成员函数的开销。2. 类的使用者可以根据需要,通过使用友元来增加类的接口。3. 支持运算符重载。(在第三章中介绍)4.如果需要一个函数同时访问多个类,则它可以定义为多个类的友员函数。例如实现矩阵类对象和向量类对象相乘的函数矩阵类对象和向量类对象相乘的例子classMatrix;classVector{ floatv[4]; //... friendVectoroperator*(constMatrix&,constVector&);};classMatrix{ Vectorv[4]; //... friendVectoroperator*(constMatrix&,constVector&);};矩阵对象和向量对象相乘的例子(续)Vectoroperator*(constMatrix&m,constVector&v){ Vectorr; for(inti=0;i<4;i++){//r[i]=m[i]*v; r.v[i]=0; for(intj=0;j<4;j++)r.v[i]+=m.v[i].v[j]*v.v[j]; } returnr;}特别注意使用友元破坏了封装和数据隐藏,导致程序的可维护性差。因此使用友元时要慎重地权衡得失。

4.8.2友元类若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。定义语法:将友元类名在另一个类中使用friend修饰说明。友元类举例classA{friendclassB;public:voidDisplay(){cout<<x<<endl;}private:intx;}classB{public:voidSet(inti);voidDisplay();private:

Aa;};友元类举例(续)voidB::Set(inti){

a.x=i;}voidB::Display(){a.Display();}友员类的成员函数可以象友员函数一样访问该类的所有成员。

【例4.14】友元类的应用classnode{ //节点类//public: node(intd,node*n=0) { data=d; next=n; }

friendclasslist;

//链表类list是节点类node的友元类private: intdata; node*next;};【例4.14】友元类的应用(续1)classlist{//链表类public: list() { head=tail=0; node_count=0; } ~list()//释放链表全部

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论