类和对象电子教案_第1页
类和对象电子教案_第2页
类和对象电子教案_第3页
类和对象电子教案_第4页
类和对象电子教案_第5页
已阅读5页,还剩190页未读 继续免费阅读

下载本文档

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

文档简介

类和对象1面向对象的思想班级对象:班级的静态特征,所属的系和专业、班级的人数,所在的教室等。这种静态特征称为属性;班级的动态特征,如学习、开会、体育比赛等,这种动态特征称为行为。按钮对象:按钮的内容、大小,按钮的字体、图案等等针对按钮的各种操作,创建、单击、双击、拖动等软件工程追求的目标之一就是可维护性,可维护性主要表现在3个方面:

可理解性、可测试性和可修改性。

面向对象的主要好处就是显著的改善了软件的可维护性。为了完成计算,必须设计出一个计算方法或解决问题的过程。因此,软件设计的主要工作就是设计求解问题的过程以OO为例,对应于软件开发的过程,OO衍生出3个概念:OOA、OOD和OOP。采用面向对象进行分析的方式称为OOA,采用面向对象进行设计的方式称为OOD,采用面向对象进行编码的方式称为OOP。面向过程(OP)和面向对象(OO)本质的区别在于分析方式的不同,最终导致了编码方式的不同。蛋炒饭盖浇饭面向过程(OP)与面向对象(OO)面向对象的软件工程面向对象的软件工程是面向对象方法在软件工程领域的全面应用。它包括:面向对象的分析(OOA)面向对象的设计(OOD)面向对象的编程(OOP)面向对象的测试(OOT)面向对象的软件维护(OOSM)4

从面向过程到面向对象

在结构化程序设计中,采用的是“自顶向下,逐步细化(divideandconquer,stepwiserefinement)”的思想。它的具体操作方法是模块化,是按功能来分的,所以也称功能块。也就是从一般事物中抽象出来的操作,在C++中称为一个函数,一个函数解决一个问题,即实现一个功能或一个操作。当程序规模和复杂性达到一定程度时不可避免地引入大量的全局变量,优良的模块化没法坚持到底。在模块化的思想中已经出现了封装的概念,这个封装是把数据封装到模块中,即局部变量。但这是很不彻底的,因为模块是功能的抽象,而数据则是具有其个性的,一但发生那怕是一点变化,抽象的功能模块就不再适用了。如一个管理软件,管理规则变化了,则管理模块以及所有与之有联系的模块都必须更改,必须重新进行功能抽象,必须重新建立模块间联系的规则。可维护性差成了制约结构化程序设计应用的瓶颈。

从面向过程到面向对象对象的概念是面向对象技术的核心所在。面向对象技术中的对象就是现实世界中某个具体的物理实体在计算机逻辑中的映射和体现。

一部移动电话,它是现实世界中的一个实体。它由天线、发射部件、接收部件、显示屏、按键、专用集成电路芯片及外壳组成;它有着其实在的功能,可以打电话,可以发短消息,可以存储、输入和编辑各种个人信息,甚至可以上网。这样一个实体可以在计算机世界中映射为一个计算机可以理解、可以操纵、具有前面所叙述的属性和操作的对象。

一辆自行车,它由车架、车轮、脚踏和传动机构、变速机构等组成,它具有代步功能,它可以进行变速骑行,特别要强调的是它有一些特征可以把你的这辆自行车与其他自行车区分开来,其中最重要的是钢印号。这些都可以在面向对象的程序中用对象及其属性和操作模拟出来。从面向过程到面向对象对象类计算机世界实体抽象类别现实世界客观世界抽象抽象实例化映射主观世界图1对象、实体与类

现实世界中的实体可以抽象出类别的概念。对应于计算机世界就有一个类(class)的概念,因为类是一个抽象的概念的对应体,所以计算机不给它分配内存,只给对象分配内存。图1表达了计算机世界与现实世界之间的对应关系。每个对象都属于一个特定的类型2面向对象程序设计的特点四个基本特征:抽象(类)(数据抽象+代码抽象)封装(过程和数据封藏起来)继承(基类和派生类)多态性(虚函数、函数重载)抽象(abstraction)抽象是对具体对象(问题)进行概括,抽出这一类对象的公共性质并加以描述的过程。先注意问题的本质及描述,其次是实现过程或细节。数据抽象(dataabstraction):描述某类对象的属性或状态(对象相互区别的物理量)。它是以数据为中心,及其能对其实施的操作作为一个整体(对象)来进行描述。OOP所基于的抽象就是数据抽象。代码抽象(过程抽象,proceduralabstraction):描述某类对象的共有的行为特征或具有的功能。就是指把程序的一些功能抽象为子程序。它对数据和操作的描述是分离的。抽象的实现:通过类的声明。9抽象实例——钟表数据抽象:数据描述:intHour,intMinute,intSecond代码抽象:SetTime(),ShowTime()10抽象实例——钟表类classClock{

public:voidSetTime(intNewH,intNewM,

intNewS);

voidShowTime();

private:intHour,Minute,Second;};11抽象实例——人数据抽象:数据描述:char*name,char*gender,intage,intid代码抽象:生物属性角度:GetCloth(),Eat(),Step(),…社会属性角度:Work(),Promote(),…12封装(encapsulation)它是与抽象相关的概念。抽象考虑的是程序实体的外部行为,而封装考虑的则是它的内部实现。封装把过程和数据隐藏起来,将它们视为一个整体,对数据的访问只能通过已定义的界面。过程封装:是指对数据封装,它通过子程序(即函数)实现。但是操作所需数据是公开的,缺乏对数据的保护。数据封装:是把数据和操作封装起来,使使用者看不到数据表示,只能通过相应操作来操作数据。在OOP中通过对象属性实现了数据及其操作的封装封装为信息隐藏提供了支持。目的是加强安全性和简化编程,使用者不必了解具体的实现细节,而只需要通过外部接口,以特定的访问权限,来使用类的成员。实现封装:类声明中的{}14封装实例:classClock{

public:voidSetTime(intNewH,intNewM,

intNewS);

voidShowTime();

private:intHour,Minute,Second;};特定的访问权限外部接口15继承与派生连接类与类的层次模型,利用现有类派生出新类的过程称为类继承,支持代码重用、提供了无限重复利用程序资源的途径、节省程序开发的时间和资源,是OOP的关键。基类和派生类实现:声明派生类16多态性(polymorphism)多态:发出同样的消息被不同类型的对象接收时导致完全不同的行为,既单一接口,多种实现,是OOP的重要补充。在程序设计中多态性体现为:一名多用指在同一作用域中一个名字有多种含义,主要通过重载(overloading)来实现,包括函数名重载和操作符重载。类属性指一个程序实体能对多种类型的数据进行操作或描述的特性,能具有类属性的程序实体通常有类属函数和类属类型。类属函数通过指针和函数模板实现,类属类型通过共用体和类模板实现。17在OOP中,由于类之间可以有继承关系,因此还存在下面的多态:对象类型的多态:子类对象既属于子类,也属于父类对象标识的多态:父类的引用或指针既可引用或指向父类,也可引用或指向子类。消息的多态:父类和子类对一个公共的消息集有不同的解释。多态的好处:易于实现程序高层代码复用,增强语言的可扩充性与多态相关的概念是联编(或绑定,binding)联编:指对多态元素使用的过程,即确定对多态元素的某个使用是多态元素的哪一种形式(如对重载函数的绑定)。分为:静态联编(staticbinding):指在编译时刻确定对多态元素的使用。如对象标识符的多态和消息的多态。动态联编(dynamicbinding):指在运行时刻来解决这个问题。如虚函数。183面向对象编程:

将数据及对数据的操作方法放在一起,作为一个相互依存、不可分离的整体---对象。对同类型对象抽象出其共性,形成类。类中的大多数数据、只能用本类的方法处理。类通过有限的接口与外界发生关系,对象与对象之间通过消息进行通信。程序=对象+对象+对象+对象+.......对象=数据+方法

优点:程序模块间的关系更为简单,程序模块的独立性、数据的安全性就有了良好的保障。通过继承与多态性,可以大大提高程序的可重用性,使得软件的开发和维护都更为方便。适合于大程序长时间的开发工作对象的两个要素静态特征(属性)动态特征(行为)班级作为一个对象班级所属系、专业、学生人数、所在的教室等。学习、开会、体育比赛等。如果想从外部控制班级中学生的活动,可以从外界向班级发一个信息(如听到广播声就去上早操,听到打铃就下课等)称为消息。对象之间通过发送和接收消息互相联系。消息包括:目标对象,请求的方法,参数在面向对象程序设计中,类和对象是两个最基本的概念。对象是客观事物在计算机中的抽象描述;类则是对具有相似属性和行为的一组对象的统一描述。例如,一个班级中的每一位同学都是一个客观事物,可以在计算机中进行抽象的描述,称为对象;而这些对象都具有学号、姓名、性别、年龄等相似的属性以及查询成绩、修改所选课程等相似的行为,为此可以将这些学生对象进行统一的描述,即定义成学生类。216.1类类是把各种不同类型的数据和对数据的操作组织在一起而形成的用户自定义的数据类型。其中,把不同类型的数据称为数据成员,把对数据的操作称为成员函数类的声明类的定义成员函数的定义数据成员的定义2201.为什么要引入类工资单:姓名、单位、编号.....成绩单:姓名、学号、各科成绩.....简单数据单元无法将这样记录作为一个数据单位处理一一对应关系[引例]定义一有关职工工资信息的结构类型,对某职工的该类信息进行处理。职工信息包括姓名、基本工资、岗位工资和职务工资;对该类信息的处理包括输入、输出和求总收入情况。

#include"iostream.h"structstuff{charname[10];doublejw,gw,zw;}s;voidprint(){cout<<<<":"<<s.jw<<'\t'<<s.gw<<'\t'<<s.zw<<endl;}voidinput(){cin>>>>s.jw>>s.gw>>s.zw;}doublesalary(){doublesum=0;sum=sum+s.jw+s.gw+s.zw;returnsum;}voidmain(){input();print();cout<<salary();}结构化的方法结构化程序的框架结构化程序#include"iostream.h"classstuff{private:charname[10];doublejw,gw,zw;public:voidprint(){cout<<name<<":"<<jw<<'\t'<<gw<<'\t'<<zw<<endl;}voidinput(){cin>>name>>jw>>gw>>zw;}};doublesalary(){doublesum=0;sum=sum+jw+gw+zw;returnsum;}voidmain(){stuffs;s.input();s.print();cout<<s.salary();}以面向对象的方式实现02.以工资单为例月份工号姓名工作部门工作时间基本工资2002.90002张三长安大学1990.7.11000校内津贴岗位津贴水电费房租实发工资20030050150130003.类的层次关系基本工资:double岗位津贴:double劳保福利:double独生子女:double房租:double电费:double水费:double取暖费:double保育费:double实发工资:double类(Class)----数据类型类是具有相同属性和相同的方法的对象的集合,它是一种既包含数据又包含函数的抽象数据类型。类是将一类对象和其他对象区别开来的一组描述。类是对象集合的抽象,对象是类的一个实例。类具有封装和隐藏性、还具有继承性。类属于类型的范畴类的种类

用struct关键字声明的类用union关键字声明的类用class关键字声明的类类的定义1.数据成员(DataMember),即类的属性;2.成员函数(MemberFunction),即类的方法;3.构造函数(constructor)和析构函数(destructor);4.运算符函数。对象(Object)----数据对象是包含现实世界物体特征的抽象实体,反映了系统为之保存信息和(或)与之交互的能力。对象=数据+作用于这些数据上的操作

=属性(Attribute)+方法(Method)类和对象的关系:说明:类和对象的关系相当于普通数据类型与其变量的关系。对象是类类型的变量

消息(Message)----函数调用消息是向某对象请求服务的一种表达方式。对象之间通过传递消息来实现相互的通信。6-1-1类的定义C++中,类定义包括类说明和类实现两大部分。说明部分提供了对该类所有数据成员和成员函数的描述;实现部分则提供了所有成员函数的实现代码。34类定义的一般形式为:class类名

{private:

数据成员或成员函数protected:

数据成员或成员函数public:

数据成员或成员函数};<各成员函数的实现代码>35说明(1)类声明以关键字class开始,其后跟类名。(2)类所声明的内容用花括号括起来,这一对花括号“{}”之间的内容称为类体。(3)类中定义的数据和函数分别是数据成员和成员函数36如:下例中定义了一个描述学生的类。classStudent{private: //private成员

intnumber; //数据成员,表示学号

charname[20]; //数据成员,表示姓名

charsex; //数据成员,表示性别

intage; //数据成员,表示年龄public: //public成员

voidset(inta,char*b,charc,intd); //成员函数,用于给各数据成员赋值

voidshow(); //成员函数,用于显示各数据成员的值};376.1.2类成员的访问权限类成员具有不同的访问权限,具体可以分成三个部分,即私有部分(private)、公有部分(public)和保护部分(protected),并分别由private、public和protected这三个关键字后跟冒号“:”来指定。这三种访问权限控制符可以以任何顺序出现,且在同一个类的定义中,这三个部分并非必须同时出现。(1)private部分:类的private部分说明的数据成员和成员函数在类之外是不能访问的,只有类中的成员函数才能访问private部分的数据成员和成员函数。38(2)protected部分:类的protected部分说明的数据成员和成员函数是不能在类之外访问的,只有类的成员函数及其子类(派生类)可以存取protected部分的成员。(3)public部分:类的public部分说明的数据成员和成员函数可以被程序中的任何函数或语句访问。public部分的成员多为成员函数,用来提供一个与外界的接口,外界只有通过这个接口才可以实现对private部分成员的访问。(4)在定义类时,当未指明成员是属于哪部分时,默认是属于private部分,但一般不提倡采用默认形式。39私有成员公有成员保护成员类内函数可以调用可以调用可以调用类外函数不可调用可以调用不可调用私有函数公有函数保护函数类内函数可以调用可以调用可以调用类外函数不可调用可以调用不可调用实例6-1定义一个Person类,用来说明人员类对象//Example6-1:定义Person类,可以存为person.hclassPerson{private://可省略 char m_strName[20]; int m_nAge; int m_nSex;public: void Register(char*name,intage,charsex); char* GetName(); int GetAge(); char GetSex(); void ShowMe();};成员函数成员数据:与一般的变量声明相同,但需要将它放在类的声明体中,在类外不能直接使用,必须通过成员函数进行设置。【实例6-2】定义日期类classTdate//定义日期类{intmonth;intday;intyear;public:

//定义公有成员函数

voidSet(intm,intd,inty);//置日期值

intIsLeapYear();//判是否闰年

voidPrint();//输出日期值};6.1.3类的数据成员类的数据成员描述的是类所表达的问题的属性。数据成员要在类的类体中进行定义,其定义的方法和一般变量的定义方法相同,但对于数据成员的访问要受到访问权限(private、protected或public)的控制。在定义类的数据成员时,要注意一个问题:在类体中不允许对类的数据成员初始化。例如:classStudent{private: intnumber; charname[20]; charsex;

intage=20; //错误的public: voidset(inta,char*b,charc,intd); voidshow(); };436.1.4类的成员函数类的成员函数描述的是类所表达的问题的行为。类中的所有成员函数都要在类的类体中进行说明,但成员函数的定义既可以在类体中给出,也可以在类体外给出。44成员函数的定义成员函数是程序算法实现部分,是对封装的数据进行操作的唯一途径。在类中说明原形,可以在类外给出函数体实现,并在函数名前使用类名加以限定。也可以直接在类中给出函数体,形成内联成员函数。允许声明重载函数和带默认形参值的函数成员函数的分类外联函数:内联函数:在类定义体中声明而在类外定义的成员函数。内联函数是指程序在编译时将函数的代码插入在函数的每个调用处,作为函数体的内部扩展,inline(1)外联函数在类外定义成员函数的具体形式为:返回值类型

类名::成员函数名(形式参数表){

函数体}作用域运算符其中“∷”是作用域运算符,“类名”是成员函数所属类的名字,“∷”用于表明其后的成员函数是属于这个特定的类。换言之,“类名∷成员函数名”的意思就是对属于“类名”的成员函数进行定义,而“返回类型”则是这个成员函数返回值的类型。余下的工作就是定义成员函数的函数体。实例6-1编写Person类成员函数的定义。//Example6.1:Person类成员函数的定义,可以存为person.cppvoidPerson::Register(char*name,intage,charsex){ strcpy(m_strName,name); m_nAge=age; m_nSex=(sex=='m'?0:1);}char*Person::GetName(){ returnm_strName;}intPerson::GetAge(){ returnm_nAge;}charPerson::GetSex(){return(m_nSex==0?'m':'f');}voidPerson::ShowMe(){cout<<GetName()<<'\t'<<GetAge()<<'\t'<<GetSex()<<endl;}voidTdate::Set(intmm,intdd,intyy)

//置日期值{month=(mm>=1&&mm<=12)?mm:1; year=(yy>=1900&&yy<=2100)?yy:1900; day=(dd>=1&&dd<=31)?dd:1;}intTdate::IsLeapYear()//判是否闰年{return(year%4==0&&year%100!=0)||(year%400==0);}

实实例6-2日期成员函数定义voidTdate::Print()//输出日期值{cout<<month<<”/”<<day<<”/”<<year<<endl;}#include<iostream.h>#include"Tdate.h"(2)内联成员函数(内部函数、内置函数)

①在类定义体内定义内联函数

classTdate{public:voidSet(intmm,intdd,intyy)//置日期值

{month=(mm>=1&&mm<=12)?mm:1; year=(yy>=1900&&yy<=2100)?yy:1900; day=(dd>=1&&dd<=31)?dd:1;}intIsLeapYear()//判是否闰年return(year%4==0&&year%100!=0)||(year%400==0);}voidPrint()//输出日期值

{cout<<month<<”/”<<day<<”/”<<year<<endl;}private:intmonth;intday;intyear;};实实例6-1的内置函数classPerson{private: charm_strName[20]; intm_nAge;intm_nSex;public: void Register(char*name,intage,charsex) { strcpy(m_strName,name); m_nAge=age; m_nSex=(sex=='m'?0:1); } char* GetName() { returnm_strName; } int GetAge() { returnm_nAge; } charGetSex() { return(m_nSex==0?'m':'f');}};②使用关键字inline定义内联成员函数inlinevoidTdate::Set(intmm,intdd,intyy)//置日期值{month=(mm>=1&&mm<=12)?mm:1; year=(yy>=1900&&yy<=2100)?yy:1900; day=(dd>=1&&dd<=31)?dd:1;}

或voidinlineTdate::Set(intmm,intdd,intyy)

//置日期值{…省略…}成员函数定义在类内和类外有个重要区别:类内定义的函数被当做内联函数,而类外定义的函数缺省当做非内联函数。inline函数值类型类名::函数名(参数)

{…}在C++语言中,结构体类型只是类的一个特例。结构体类型与类的唯一的区别在于:在类中,其成员的缺省的存取权限是私有的;而在结构体类型中,其成员的缺省的存取权限是公有的。类实际上是一种抽象机制,它描述了一类问题共同的属性和行为。类名仅提供一种类型定义,只有在定义属于类的变量后,系统才会为其预留空间,这种变量称为对象,它是类的实例。

对象是面向对象方法的主体,当一个对象映射为软件实现时由三部分组成:私有的数据结构:它用于描述对象的内部状态。处理:也称为操作或方法,它施加于数据结构之上。接口:这是对象可被共享的部分,消息通过接口调用相应的操作。接口规定哪些操作是允许的。它不提供操作是如何实现的信息。类的变量我们称之为对象。6.2对象6.2.1对象的定义

类名对象名(参数表);类是抽象的概念,而对象是具体的概念;每个对象占用了各自的存储单元,每个对象都各自具有了该类的一套数据成员(静态成员除外),而所有成员函数是所有对象共有的。每个对象的成员函数都通过指针指向同一个代码空间。数据区对象n......公共代码区数据区对象n数据区对象n55classlocation{private:intx,y;public:voidinit(intx0,inty0);intGetx(void);intGety(void);}dot1,dot2;例(1)方法一:在定义类的同时直接定义(2)方法二:在使用时定义对象说明对象的一般形式(类的实例化):

如,假设A是一个已定义过的类,则可定义:

指向A类对象的指针A类的对象对象数组《存储类型》类名对象名表;

Aa1,*p,a[5]类名*指针变量名表;Studentstu; //定义Student类的对象stuStudent*p; //定义指向Student类的指针p=&stu; //使指针变量p指向对象stu

6.2.2对象的指针586.2.3访问对象的成员格式1:对象名.成员名格式2:指针变量名->成员名格式3:(*指针变量名).成员名【例6-1】访问对象的成员。59类内:(成员函数访问数据成员)

直接访问类成员类外:(外部程序访问类的公有数据成员或成员函数)

对象成员访问方式同结构成员,对象的成员表示如下:

<对象名>.<成员名>或者

<对象名>.<成员名>(<参数表>)前者用于表示数据成员,后者用于表示成员函数。这里的“.”是一个运算符,该运算符的功能是表示对象的成员。类成员的访问方式

指向对象的指针的成员表示如下:

<对象指针名>-><成员名>或者

<对象指针名>-><成员名>(<参数表>) 同样,前者用于表示数据成员,后者用于表示成员函数。这里的“->”是一个表示成员的运算符,它与前面介绍过的“.”运算符的区别是:“->”用来表示指向对象的指针的成员,而“.”用来表示一般对象的成员。 对于数据成员和成员函数,以下两种表示方式是等价的:

<对象指针名>-><成员名>与 (*<对象指针名>).<成员名>说明:在建立对象时,只为对象分配用于保存数据成员的内存空间,而成员函数的代码为该类的每一个对象所共享。用成员选择运算符“.”只能访问对象的公有成员,而不能访问对象的私有成员或保护成员。若要访问对象的私有的数据成员,只能通过对象的公有成员函数来获取。在类的作用域外,禁止直接访问一个对象中的私有成员同类对象之间可以整体赋值例如:Person对象的引用Personperson1,person2;

intnage=person1.GetAge();person1=person2;Person*p;p=newPerson;nage=p->GetAge();对象可以作函数的入口参数(实参、形参),也可以作函数的出口参数,也可以作引用参数。这与一般变量作为函数的参数是完全相同的。可以定义类类型的指针,类类型的引用,对象数组,指向类类型的指针数组和指向一维或多维数组的指针变量,但不能取私有数据成员的指针或成员函数的地址一个类的对象,可作为另一个类的成员实例6-1完整的人事资料输入输出程序。//Example6-1:人事资料的输入和输出#include“person.h"voidmain(){ charname[20],sex;intage;Personperson1,person2;//定义对象 cout<<"Enteraperson'sname,ageandsex:"; cin>>name>>age>>sex; person1.Register(name,age,sex);//对象的引用 cout<<"person1:\t";person1.ShowMe(); person1.Register("Zhang3",19,'m'); cout<<"person1:\t";person1.ShowMe(); person2=person1; //对象之间的赋值

cout<<"person2:\t";person2.ShowMe();Person*p;p=newPerson;age=p->GetAge();}相当于成员数据间相互赋值实例6-2#include<iostream.h>#include"Tdate.h"voidmain(){ Tdatedate1,date2; Tdate*p; p=&date2; date1.Set(10,20,2012); if(date1.IsLeapYear()) {date1.Print();cout<<"isleapyear";} else cout<<"isn't"; date2.Set(10,1,2008); p->Print();}【实例6-3】类的应用举例#include<iostream.h> classClock //时钟类的声明{public: //外部接口,公有成员函数

voidSetTime(intNewH,intNewM,intNewS); voidShowTime();private: //私有数据成员

intHour,Minute,Second;};//时钟类成员函数的具体实现voidClock::SetTime(intH,intM,intS){ Hour=H; Minute=M; Second=S;}voidClock::ShowTime(){ cout<<Hour<<":"<<Minute<<":"<<Second<<endl;}voidmain(){ClockmyClock; myClock.Hour=20;myClock.Minute=40;myClock.Second=26;cout<<myClock.Hour<<":"<<myClock.Minute<<":"<<myClock.Second<<endl;}调用对吗?问题:那么如何初始化私有成员?非法,私有成员不能在类外访问必须通过类内公有函数访问私有数据成员6.2.4this指针在类的每一个成员函数的形参表中都有一个隐含的指针变量this,该指针变量的类型就是成员函数所属类的类型。当程序中调用类的成员函数时,this指针变量被自动初始化为发出函数调用的对象的地址。尽管在定义成员函数时没有看到也没有定义this指针变量,但在成员函数的函数体内可以使用this指针变量。this->number=a;strcpy(this->name,b);this->sex=c;this->age=d;

676.2.5对象的作用域与生存期(放最后讲)对象是类的实例,它的实质就是某种数据类型的变量。classDesk //定义Desk类{ public: intweight; inthigh; intwidth; intlength;};classStool //定义Stool类{ public: intweight; inthigh; intwidth; intlength;};Deskda; //定义全局对象Stoolsa;voidfn(){ staticStoolss; //定义局部静态对象

Deskda; //定义局部对象

//……}681.局部对象局部对象(不包括局部静态对象)的作用域是定义它的函数体,其生存期是从函数调用开始到函数调用结束,当下一次重新调用函数时,再重新构造对象。构造局部对象的次序(即分配存储单元的次序)是按照它们在函数体中声明的次序进行的。局部对象所占的存储空间被分配在程序的动态区(栈区)中。692.静态对象静态对象(局部静态对象和全局静态对象)的作用域是定义它的函数体或程序文件,其生存期是整个程序的运行时间。构造静态对象的次序是按它们在程序中出现的先后次序,且在整个程序运行开始时(即在主函数运行前)只构造一次。静态对象所占的存储空间被分配在程序的静态区(全局区)中。703.全局对象全局对象的作用域是整个程序,其生存期是整个程序的运行时间。它也是在程序运行前(即在主函数运行前)只构造一次。全局对象所占的存储空间被分配在程序的静态区(全局区)中。4.类中成员的构造次序类中成员的构造次序是以类中声明成员的次序进行的。71实例具有静态、动态生存期对象的时钟程序#include<iostream>usingnamespacestd;classClock //时钟类声明{public: //外部接口

Clock(); voidSetTime(intNewH,intNewM,intNewS);//三个形参均具有函数原型作用域

voidShowTime(); ~Clock(){}private: //私有数据成员

intHour,Minute,Second;};//时钟类成员函数实现Clock::Clock() //构造函数{ Hour=0; Minute=0; Second=0;}voidClock::SetTime(intNewH,intNewM,intNewS){ Hour=NewH; Minute=NewM; Second=NewS;}voidClock::ShowTime(){ cout<<Hour<<":"<<Minute<<":"<<Second<<endl;}20Clock

globClock; //声明全局对象globClock,

//具有静态生存期,文件作用域voidmain() //主函数{ cout<<"Firsttimeoutput:"<<endl;

//引用具有文件作用域的对象:

globClock.ShowTime();//对象的成员函数具有类作用域

globClock.SetTime(8,30,30); ClockmyClock(globClock);//声明具有块作用域的对象myClock cout<<"Secondtimeoutput:"<<endl;

myClock.ShowTime(); //引用具有块作用域的对象}216.3构造函数与析构函数在声明对象的同时给它的数据成员赋初值,称为对象的初始化。在对象使用结束时,还经常需要进行一些清理工作。C++中对象的初始化和清理工作分别由两个特殊的成员函数来完成,即构造函数和析构函数。75构造函数(Constructor)定义了创建对象的方法,提供了初始化对象的一种简便手段。构造函数的作用是:为对象分配空间;对数据成员赋初值;请求其他资源。

对象的初始化是指对象数据成员的初始化,在使用对象前,一定要初始化.

对对象初始化的方法普通成员函数来初始化构造函数对对象进行初始化使用上的不便和不安全6.3.1构造函数构造函数(Constructor)是与类名同名的特殊的成员函数;构造函数的执行是在系统创建对象时自动执行。构造函数的定义格式为:类名(形参说明){函数体}【例6-2】修改【例6-1】中的Student类,为其定义构造函数,完成对象的初始化工作。77对于对象的初始化,采用构造函数(constructor)。当需要对对象进行初始化时,总是编写一个或一组构造函数。构造函数是特殊的公有成员函数,其特征如下:函数名与类名相同。构造函数无函数返回类型说明。注意是没有而不是void,即什么也不写,也不可写void!实际上构造函数有返回值,返回的就是构造函数所创建的对象。是成员函数,可写在类体内,亦可写在类体外。在程序运行时,当新的对象被建立,该对象所属的类的构造函数自动被调用,在该对象生存期中也只调用这一次。构造函数可以重载。严格地讲,说明中可以有多个构造函数,它们由不同的参数表区分,系统在自动调用时按一般函数重载的规则选一个执行。构造函数允许参数缺省调用,允许为内联函数、带默认形参值的函数new操作符自动调用生成对象的构造函数实例6-4构造函数举例classClock{public:

Clock(intNewH,intNewM,intNewS);//构造函数

voidSetTime(intNewH,intNewM,intNewS); voidShowTime();private: intHour,Minute,Second;};79构造函数的实现:Clock::Clock(intNewH,intNewM,intNewS){ Hour=NewH; Minute=NewM; Second=NewS;}//其他函数略建立对象时构造函数的作用:voidmain(){

Clockc(12,0,0);//隐含调用构造函数,将初始值作为实参。

c.ShowTime();}316.3.2构造函数的重载如果一个类中出现了两个以上的同名成员函数,则称为类的成员函数的重载。一个类的成员函数和另一个类的成员函数同名;一个类的成员函数与一个类外的函数同名;81在类的成员函数的重载中,比较常见的形式是构造函数的重载。在对象定义时,系统会根据参数的类型及个数选择调用某一种合适的构造函数完成对象的构造。【例6-3】修改【例6-2】中的Student类,为其定义两个构造函数:一个无参构造函数和一个有参构造函数,完成对象的初始化工作。82实例6-4构造函数重载举例classClock{public:

Clock();//构造函数Clock(intNewH,intNewM,intNewS);//重载构造函数

voidSetTime(intNewH,intNewM,intNewS); voidShowTime();private: intHour,Minute,Second;};83构造函数的实现:Clock::Clock(){ Hour=0; Minute=0; Second=0;}Clock::Clock(intNewH,intNewM,intNewS)//重载构造函数{省略}//见前面建立对象时各构造函数的作用:voidmain(){

Clockc1,c2(12,12,35);//隐含调用构造函数,将初始值作为实参。

c1.ShowTime();c2.ShowTime();}316.3.3默认构造函数C++规定,每个类必须有一个构造函数,没有构造函数就不能创建对象。如果在类中没有显式定义构造函数,则C++编译系统在编译时为该类提供一个无参的默认的构造函数。只要一个类定义了一个构造函数,则C++编译系统就不再提供默认的构造函数。也就是说,如果已经为类定义了一个带参数的构造函数,但还想要无参构造函数,则必须自己定义。默认构造函数初始化数据成员时常赋予默认值85当构造函数的参数具有默认值时,称为具有缺省参数的构造函数。【例6-4】修改【例6-1】中的Student类,定义有默认参数的构造函数,完成对象的初始化工作。使用默认值时要注意以下几点:(1)只能出现在类定义的接口部分,而不能出现在类定义的实现部分。(2)所有具有默认值的参数必须处在参数表的最右边。(3)在使用具有缺省参数的构造函数时,要防止二义性。6.3.4有缺省参数的构造函数86classA{ floatx,y;public:A(floata=10,floatb=20){ x=a; y=b; }A(){}voidPrint(void){ cout<<x<<'\t'<<y<<endl; }};voidmain(void){ Aa1;

Aa2(3.0,30.0);}两个函数均为缺省的构造函数两个构造函数均可供调用,构造函数不唯一产生对象时,系统必定要调用构造函数。所以任一对象的构造函数必须唯一。例如:实例6-4默认参数构造函数举例classClock{public:

Clock(intNewH=12,intNewM=12,intNewS=12)//默认参数构造函数{Hour=NewH;Minute=NewM;Second=NewS;}

voidSetTime(intNewH,intNewM,intNewS); voidShowTime();private: intHour,Minute,Second;};88数据成员的初始化1.在构造函数的函数体中进行初始化(见前例)。2.在构造函数的头部初始化。3.混合初始化4.使用默认参数初始化(见前例)。891.在构造函数的函数体中初始化2.在构造函数的头部初始化(即成员初始化表)成员初始化表应放在构造函数定义中参数表的紧后方,包括冒号和一个或几个用逗号隔开的项。每一项包括数据成员名和括号内的初始化值。其格式为:

<类名>::<构造函数>(<参数表>):<变量1>(<初值1>),…,<变量n>(<初值n>)

{ …… }例如Person::Person():Age(0),Sex('m') { }

对于常量数据成员和引用数据成员(某些静态成员除外),不能在说明它们时初始化,也不能采用赋值操作对它们初始化。例如:classA{intx;constinty=1;//Errorint&z=x;//Errorpublic: A() {x=0;//Ok y=1;}//Error};对于这种初始化问题,可以在定义构造函数时,在函数头和函数体之间加入一个对数据成员进行初始化的表(称为成员初始化表,memberinitializerlist),用于对数据成员进行初始化。例如classA{intx;constinty;int&z;public:A():z(x),y(1)//成员初始化表

{x=0;}};3.混合初始化。936.3.5析构函数当一个对象定义时,C++自动调用构造函数建立该对象并进行初始化,那么当一个对象的生命周期结束时,C++也会自动调用一个函数注销该对象并进行善后工作,如释放由构造函数分配的内存等,这个特殊的成员函数即析构函数(destructor):~类名();实现:<类名>::~<缺省析构函数名>(){}功能

:

当对象被撤消时,释放该对象占用的内存空间

特点:析构函数的作用与构造函数正好相反,一般情况下,析构函数执行构造函数的逆操作。在对象消亡时,系统将自动调用析构函数。析构函数没有返回值,没有参数,一个类有一个也只有一个析构函数,这与构造函数不同。析构函数可以缺省,但是不能重载。94析构函数在以下情况下自动被调用一个动态分配的对象被删除,即使用delete删除对象时,编译系统会自动调用析构函数(2)程序运行结束时(3)一个编译器生成的临时对象不再需要当用户在程序中手工调用析构函数时,语法如下:对象名.类名::析构函数名注:而构造函数只能由系统调用,不能用上述方法调用说明:(1)如果程序员在定义类时没有为类提供析构函数,则系统会自动创建一个默认的析构函数,其形式为:~类名(){}(2)如果在类的对象中分配有动态内存,就必须为该类提供适当的析构函数,以完成清理工作。(3)一个类中只能拥有一个析构函数。(4)对象被析构的顺序与其建立时的顺序正好相反。【例6-5】分析以下程序的执行结果。96实例6-1为类Person增加构造函数和析构函数classPerson{Private: charm_strName[20];intm_nAge;intm_nSex;public: person(constchar*name,intage,charsex)//构造函数

{strcpy(m_strName,name);m_nAge=age; m_nSex=(sex==‘m’?0:1);}~Person() //析构函数

{ cout<<"NowdestroyingtheinstanceofPerson"<<endl;}}voidRegister(char*Name,intAge,charSex);void GetName(char*Name);intGetAge();charGetSex();void ShowMe();};6.3.6拷贝构造函数一种特殊的构造函数类名::类名(const类名&形式参数){函数体}说明:(1)拷贝构造函数的名称与类的名称相同,且它只有一个参数。该参数就是对该类对象的引用。(2)拷贝构造函数的功能是用于实现对象值的拷贝。通过将一个同类对象的值拷贝给一个新对象完成对新对象的初始化,即用一个对象去构造另外一个对象。【例6-6】Person类是一个人员信息类。用普通构造函数生成对象per1,用拷贝构造函数生成对象per2。

98拷贝初始化构造函数

拷贝构造函数是一种特殊的构造函数用于用已知对象初始化被创建的同类对象只有一个参数,且是对某个对象的引用常用于做函数的形参及返回值拷贝构造函数的声明形式为:类名(类名&对象名);实现:类名::类名(类名&对象名){}

【实例6-5】#include"iostream.h"classCircle1{private:doublex,y,r,s;public:voidprint(){cout<<"圆心:("<<x<<","<<y<<")"<<endl;cout<<"半径:"<<r<<endl;}Circle1(doublex1,doubley1,doubler1){x=x1;y=y1;r=r1;}Circle1(Circle1&p) {x=p.x;y=p.y,r=p.r;}};voidmain(){Circle1p1(0,0,2),p2(p1);p1.print();p2.print();}拷贝初始化构造函数(引用做参数)已知对象做初值public:cat();cat(cat&);voidplay();voidhunt();};cat::cat(cat&other){age=other.age;weight=other.weight;color=other.color;}拷贝构造函数的声明拷贝构造函数的定义【实例6-6】classcat{private:intage;floatweight;char*color;

类cat的定义调用拷贝构造函数有以下三种情况(1)用类的一个对象去初始化另一个对象时。(2)对象作为函数参数传递时,调用拷贝构造函数(3)如果函数的返回值是类的对象,函数调用返回时,调用拷贝构造函数。

例:catcat1;

catcat2(cat1);

//创建cat2时系统自动调用拷贝构造函数用cat1初始化cat2。

例f(cata){}//定义f函数,形参为cat类对象

catb;//定义对象bf(b);//进行f函数调用时,系统自动调用拷贝构造函数

catf()//定义f函数,函数的返回值为cat类的对象

{cata;

…returna;} catb;//定义对象bb=f();//调用f函数,系统自动调用拷贝构造函数

如果程序员没有为类声明拷贝初始化构造函数,则编译器自己生成一个拷贝构造函数。这个构造函数执行的功能是:用作为初始值的对象的每个数据成员的值,初始化将要建立的对象的对应数据成员。【实例6-7】构造函数中数据成员初始化的不同方法#include<iostream>usingnamespacestd;classTdate{ intday,month,year;public://构造函数,无参数,使用参数初始化表 Tdate():year(1900),month(1),day(1)

{}Tdate(intyy,intmm=1,intdd=1);//构造函数,3个参数,2个有默认值

Tdate(Tdate&d) //拷贝构造函数

{ year=d.year; month=d.month; day=d.day; } voidprint_ymd();};Tdate::Tdate(intyy,intmm,intdd):year(yy),month(mm),day(dd){}voidTdate::print_ymd(){ cout<<year<<"-"<<month<<"-"<<day<<endl;}intmain(){ Tdatedate1; //使用无参数的构造函数创建日期类对象

cout<<"date1:"; date1.print_ymd(); Tdatedate2(2010);//使用3参数的构造函数创建日期类对象,2个默认值

cout<<"date2:"; date2.print_ymd(); Tdatedate3(2010,4);//使用3参数的构造函数创建日期类对象,1个默认值cout<<"date3:"; date3.print_ymd(); Tdatedate4(2010,4,8);//使用3参数的构造函数创建日期类对象

cout<<"date4:"; date4.print_ymd(); Tdatedate5(date4); //使用拷贝构造函数创建日期类对象

cout<<"date5:"; date5.print_ymd(); return0;}6.3.7浅拷贝与深拷贝浅拷贝:由默认的拷贝构造函数将已有对象的数据成员一一赋值给同类的新对象的各数据成员来构造新对象的方法就是浅拷贝,即成员级拷贝。缺点:在程序中进行对象的复制时,也可以有选择、有变化地复制,这时默认的拷贝构造函数就不能胜任了。浅拷贝会带来数据安全方面的隐患。106深拷贝对于指针型数据成员,要拷贝的是其所指向的区域内容而不是指针值。要完成深拷贝,用户必须编写自定义的拷贝构造函数,在拷贝构造函数中先要分配存储空间,然后再将相应的内容拷贝过来,而不能采用默认的拷贝构造函数。107浅拷贝与深拷贝的处理过程:对象a对象b堆空间对象a对象b堆空间堆空间浅拷贝深拷贝【例6-7】Person是一个人员信息类。用普通构造函数生成per1,用拷贝构造函数生成per2。本例中,由对象per1生成对象per2的方法是深拷贝。108下面的程序段定义了深拷贝构造函数:

#include<string.h>classPerson{public:

Person(char*name)//构造函数

{//使用new进行动态内存分配pname=newchar[strlen(name)+1];

if(pname!=0)

{strcpy(pname,name);}}

Person(Person&p)//拷贝构造函数

{pname=newchar[strlen(p.pname)+1];//复制资源

if(pname!=0)strcpy(pname,p.pname);//复制对象空间}//其它成员函数private:char*pname;};//类定义的结束

!!!注意:由C++提供的默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝)。如果对象的数据成员包括指向堆空间的指针,就不能使用这种拷贝方式,因为两个对象都拥有同一个资源,对象析构时,该资源将经历两次资源返还,此时必须自定义深拷贝构造6.4对象成员与对象数组6.4.1对象成员定义:对象成员也称为类的聚集,是指在类的定义中数据成员可以为其它的类对象,即类对象作为成员。如果在类定义中包含有对象成员,则在创建类对象时先调用对象成员的构造函数,再调用类本身的构造函数。析构函数的调用顺序正好相反。从实现的角度讲,实际上是首先调用类本身的构造函数,在执行本身构造函数的函数体之前,调用成员对象的构造函数,然后再执行类本身构造函数的函数体。因此,在构造函数的编译结果中包含了对对象成员的构造函数的调用,至于调用对象成员的哪一个构造函数,是由成员初始化表指定的;当成员初始化表为空时,则调用对象成员110classX{

类名1成员1;

类名2成员2; ……

类名n成员n; ……};111类X的构造函数的定义格式应为:X::X(参数表0):成员1(参数表1),成员2(参数表2),…,成员n(参数表n){……}【例6-8】分析以下程序的执行结果。112【实例6-8】含有对象成员的类的构造函数和析构函数的调用顺序#include<iostream.h>#include<string.h>classStudentID{private: intvalue;public:StudentID(intid=10)//带缺省参数的构造函数{value=id;cout<<"Assigningstudentid"<<value<<endl;}~StudentID(){ cout<<"Destructingid"<<value<<endl;}};classStudent{public:Student(char*pName="noname",intssID=0):id(ssID)//Student(char*pName="noname",intssID=0){cout<<"Constructingstudent"<<pName<<endl;strncpy(name,pName,sizeof(name));name[sizeof(name)-1]='\n';}~Student(){cout<<"Deconstructingstudent"<<name<<endl;}protected:charname[20];StudentIDid;//对象成员};voidmain(){Students("wang",9902);}6.4.2对象数组数组中的每个元素都是该类中的一个对象。声明:类名数组名[数组大小];访问方法:通过下标访问数组名[下标].成员名【例6-9】定义学生类Student,完成若干个学生信息的输入和显示。115classTdate{public:Tdate(intm,intd,inty);private:…};Tdatedate[2]={Tdate(7,22,1998),Tdate(7,23,1998)};或:Tdatedate[2];date[0]=Tdate(7,22,1998);date[1]=Tdate(7,23,1998);初始化用无名对象进行。初始化的方法有两种:定义的同时初始化;

定义好之后,再单个元素初始化;对象数组初始化数组生成时,编译器调用数组中每个元素的构造函数或缺省构造函数,数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象。通过初始化列表赋值。例:Tdatedate[2]={Tdate(7,22,1998),Tdate(7,23,1998)};如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用默认构造函数)。不声明构造函数,则采用默认构造函数。各元素对象的初值要求为相同的值时,可以声明具有默认形参值的构造函数。各元素对象的初值要求为不同的值时,需要声明带形参的构造函数。当数组中每一个元素对象被删除时,系统都要调用一次析构函数。说明:数组元素赋初值时,

date[0]=Tdate(7,22,1998);

温馨提示

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

最新文档

评论

0/150

提交评论