C++程序设计第9章类和对象.ppt
第9章 类和对象 目的与要求9.1 概述9.2 类与对象9.3 构造函数9.4 析构函数9.5 构造函数和对象成员9.6 this指针本章小结目的与要求 通过本章学习,应理解类与对象的概念,掌握类与对象的定义方法,能用类描述某事物,用类定义的对象对该事物进行处理。了解构造函数与析构函数的概念及作用,掌握构造函数与析构函数的定义格式及使用方法。初步掌握new运算符动态建立对象及用delete运算符回收对象占用空间的方法。初步掌握类中对象成员的构造函数格式及调用过程。理解this指针的概念。9.1 概述引例:【例9.1】先定义一个学生成绩的结构体类型student,再定义计算学生平均成绩的函数Average()与显示学生成绩的函数Display(),在主函数中输入学生成绩,并调用Average()计算平均成绩,调用Display()显示学生成绩。例程概述 1用结构体描述与处理事物存在的问题 (1)程序的独立性与可维护性差 (2)数据的安全性差 2解决问题的方法 采用面向对象的程序设计方法(简称OOP)面向对象的程序设计方法是将描述某类事物的数据与处理这些数据的函数封装成一个整体,称为类。9.2 类与对象 9.2.1类 1.类的定义 (1)类是由描述某类事物的数据和处理数据的函数组成的导出数据类型。(2)类的成员有两种:数据成员、成员函数。2.定义格式类的定义格式为:classprivate:/定义私有数据成员或成员函数public:/定义公有数据成员或成员函数protected:/定义保护数据成员或成员函数;3.成员访问权限 private:定义私有成员。私有数据成员只允许类内函数访问,私有成员函数只允许在类内调用。类外函数不允许访问私有数据成员,也不允许调用私有成员函数。成员访问权限public:定义公有成员。公有数据成员允许类内或类外的函数访问,公有成员函数允许在类内或类外调用。成员访问权限protected:定义保护成员。保护数据成员只允许类内或其子类中函数访问,保护成员函数允许在类内或其子类中调用。类的定义举例【例9.2】定义学生成绩类Student,其数据成员与成员函数如下。(1)描述学生成绩的私有数据成员为:姓名(Name8)、物理(Phi)、数学(Math)、平均成绩(Ave);(2)处理学生成绩的公有成员函数为:输入学生成绩成员函数:Input();计算平均成绩成员函数:Average();显示学生成绩成员函数:Display();输出学生成绩成员函数:Output()。例程例程4.类的特点(1)类具有封装性(2)类具有安全性(3)类具有独立性与可维护性。(4)类具有继承性(5)类具有重载性与多态性 5.类的说明(1)类的成员默认的访问权限为私有。(2)成员函数可在类体内作引用性说明,类体外作定义性说明。在类体外定义成员函数的格式为:(形参表)函数体(3)关键词public、private、protected在类中使用先后次序无关紧要,且可使用多次。类的说明(4)数据成员与成员函数在类中的定义次序无关紧要。(5)因为类是一种数据类型,系统并不会为其分配内存空间,所以在定义类中的数据成员时,不能对其进行初始化,也不能指定其存储类型。(6)类和结构体的关系 结构体与类唯一区别在于:在类中,其成员的缺省存取权限是私有,而在结构体类型中,其成员的缺省权限是公有。9.2.2 对象 用类定义的变量称为对象。1.对象的定义格式存储类型,;例如:Studentstu1,stu2;2.存储空间的分配 定义类时系统并不为类分配内存空间,仅当用类定义对象时系统才为对象分配内存空间。为对象分配的存储空间大小取决于在定义类时所定义的成员类型和数量。在创建对象时,类被用作样板,因此对象也称为实例。在建立不同对象时,系统只为数据成员分配不同内存空间,而不同对象的成员函数则共享同一内存空间。3.定义对象的三种方式(1)先定义类,后定义对象,例如:Studentstu1,stu2;(2)定义类的同时定义对象,例如:classStudentstu1,stu2;(3)直接定义对象,例如:classstu1,stu2;4.对象成员的引用 每个由类定义的对象都有若干个数据成员与成员函数,在类外通过对象可以使用公有的数据成员及成员函数。(1)引用数据成员的格式为:(2)调用成员函数的格式为:(实参)注意:在类内引用本类的数据成员或调用本类的成员函数时,不能加对象名。5.对象的赋值运算 同类对象之间所能进行的惟一运算就是赋值运算,赋值运算的格式为:=;【例9.4】定义描述学生成绩的类Student,然后用Student定义一个学生对象stu。在主函数中输入学生的姓名、物理、数学,并调用成员函数Input()输入到数据成员Name、Phi、Math中,调用成员函数Average()计算平均成绩,调用成员函数Output()将对象stu的私有数据成员返回给主函数,并显示其值。最后再调用成员函数Display()显示私有数据成员的值。例程例程 对象的使用说明(1)定义对象时,编译系统为不同对象的数据成员分配不同的内存空间;(2)不能在类外直接对其私有数据成员进行操作,如:stu.Phi=90;是错误的。而只能通过公有成员函数Input()对其进行赋值操作,这样就保证了数据的安全性。(3)对象允许进行赋值运算。同类对象的赋值运算将stu1的所有数据成员值赋给stu2对应的数据成员。(4)对象作为函数参数时,属于值传送。只能通过函数返回对象值,而不能通过形参返回运算结果。(5)在定义对象时可对公有数据成员进行初始化赋值,但不能对私有或保护数据成员进行初始化赋值。私有或保护数据成员只能通过构造函数进行初始化赋值。9.3 构造函数 构造函数主要作用是在定义对象时,对其数据成员进行初始化。9.3.1 构造函数的定义1.构造函数的定义格式 :()构造函数体注意:构造函数的函数名必须与类名相同,且无返回数据类型。若在类内定义构造函数,则“:”可省去。【例9.5】在【例9.4】的学生成绩类Student中定义构造函数,用构造函数实现对象stu1的初始化赋值。例程例程2.对构造函数的说明(1)构造函数名必须与类同名。(2)定义构造函数时,不能定义函数返回值的类型,也不能指定为void 类型。因构造函数无需返回函数值。(3)构造函数可重载,其参数可有可无,也可指定默认值。(4)若要用类定义对象,则构造函数必须是公有成员函数。若类仅用于派生其它类,则构造函数可定义为保护成员函数。9.3.2 用构造函数初始化对象的过程 用构造函数初始化对象的过程,实际上就是对构造函数的调用过程。其步骤为:(1)程序执行到定义对象语句时,系统为对象分配内存空间;(2)系统自动调用构造函数,将实参传送给形参,执行构造函数体时,将形参值赋给对象的数据成员。【例9.6】定义描述矩形的类。用构造函数完成矩形对象的初始化,编写计算矩形面积的函数,并输出矩形面积。例程例程用构造函数初始化对象时的注意点(1)定义无参对象时,对象后不能跟“()”。(2)类已定义构造函数前提下,有参(无参)对象必须定义带参(无参)构造函数 9.3.3 默认构造函数 默认构造函数有三类:(1)编译器产生的缺省构造函数(前提:用户未定义构造函数)格式:();/该构造函数什么也不做。(2)无参构造函数(3)各参数均有默认值的构造函数 默认构造函数必须惟一。9.3.4 拷贝的构造函数 用类定义对象时,可用拷贝功能的构造函数将另一个已存在对象的数据拷贝到新建的对象中。拷贝构造函数的格式为:(类名&c)函数体 【例9.7】在【例9.6】中增加一个拷贝构造函数,在主函数中用矩形类Rectangle定义对象r3,实参为r1。例程例程9.3.5 用new运算符动态定义对象 可以用new运算符来动态地建立对象。用new运算符建立对象时,同样也要自动调用构造函数,以便完成对象数据成员的初始化。【例9.8】定义一个矩形类,类中有一个带参构造函数与一个无参构造函数。用new运算符建立矩形对象时调用构造函数完成对矩形的初始化。例程例程用new运算符动态定义对象结论当用new运算符动态建立对象时,系统将:(1)为对象分配内存空间;(2)调用构造函数初始化对象的数据成员;(3)将对象起始地址返回给指针变量;(4)动态分配的对象内存空间必须用delete运算符回收。9.4 析构函数 析构函数作用是在撤消对象时,回收对象占用的内存空间。9.4.1 定义析构函数 析构函数定义格式为:()析构函数体 定义析构函数的说明说明:(1)析构函数名必须与类名相同,并在其前加“”。(以便与构造函数名相区别);(2)析构函数不能带任何参数,无返回值(函数名前不能用关键词void);(3)析构函数不允许重载,是唯一的;(3)析构函数是在撤消对象时由系统自动调用,其作用是在撤消对象前做好结束工作。9.4.2 析构函数的调用 析构函数的调用分两种情况:1.用类直接建立对象 在程序执行过程中,当遇到对象的生命期结束时,系统自动调用析构函数,然后回收为对象所分配的存储空间。【例9.9】定义描述矩形的类。用构造函数完成矩形对象的初始化,在析构函数中显示“调用析构函数”字样,编写计算矩形面积的函数,并输出矩形的面积。例程例程用类直接建立对象举例【例9.10】定义描述复数的类Complex,定义数据成员与成员函数如下。(1)私有数据成员为:实部Real,虚部Image;(2)公有成员函数为:带参构造函数:Complex(floatR=0,floatI=0);拷贝构造函数:Complex(Complex&c);显示复数的函数:Show();析构函数:Complex();(3)主函数内用复数类Complex定义对象c1(10,20);定义对象c2(c1),并调用Show()函数显示复数值。例程例程2.用new运算符动态生成对象 用new运算符动态生成的对象,在产生对象时调用构造函数,只在用delete释放对象时,才调用析构函数。【例9.11】定义一个复数类,类中有一个带参构造函数、一个无参构造函数及一个析构函数。用new运算符建立复数对象时调用构造函数完成对复数的初始化。最后用delete运算符回收对象占用的内存空间。例程例程9.4.3 默认的析构函数 若在类的定义中没有显式地定义析构函数,则编译器自动地产生一个默认的析构函数,其格式为:();9.5 构造函数和对象成员 在一个类的定义中,若定义有n个对象成员,则其构造函数的一般格式为::(形参表):(实参表1),对象名2(实参表2),(实参表n)构造函数体 其 中:(实 参 1),对 象 名 2(实 参2),(实参n)称为成员初始化表。【例9.12】定义一个描述矩形的类与一个描述长方体的类,用矩形类的对象作为长方体类的成员,讨论长方体类中的构造函数与对象成员的初始化过程。例程例程对构造函数的几点说明(1)形参必须带有类型说明,而实参是可计算值的表达式。(2)对象成员构造函数的调用顺序取决于这些对象成员在类中的说明顺序,而与它们在成员初始化表的位置无关。(3)建立类的对象时,先调用各个对象成员的构造函数,初始化相应的对象成员,然后才执行类的构造函数,初始化类中其它成员。析构函数的调用顺序与构造函数正好相反。【例9.13】说明构造函数与析构函数的调用顺序。例程例程9.6 this指针 用类定义一个对象时,系统会自动建立一个指向该对象的指针变量,该指针变量称this指针,this指针变量指向所定义的对象。this指针具有如下形式的默认说明:*constthis=&对象;本章小结 1类的定义类类:类由描述某类事物的数据(数据成员)及处理数据的函数(成员函数)组成。类的定义格式为:classpublic:/定义公有数据成员或成员函数,private:/定义私有数据成员或成员函数protected:/定义保护数据成员或成员函数成员的访问属性public(公有成员)private(私有成员)protected(保护成员)公有数据成员允许类内或类外函数访问公有成员函数允许在类内或类外调用私有数据成员只允许类内函数访问私有成员函数只允许在类内调用保护数据成员只允许类或其子类中函数访问保护成员函数允许在类内或其子类中调用2.对象对象对象:用类定义的变量称为对象。对象的定义格式:,;对象的成员分为数据成员与成员函数两类。在类外:数据成员引用方式为:.;成员函数的调用方式为:.(实参);在类内引用或调用时,不写对象名。3.构造函数与析构函数(1)构造函数用于对象数据成员的初始化。构造函数名必须与类名相同,且无返回类型。构造函数允许重载,将无参或有默认值参数的构造函数称为默认的构造函数。当用户没有定义构造函数时,系统会自动产生一个默认的构造函数,该构造函数体为空。常用的构造函数有三类:有参构造函数、无参构造函数与拷贝构造函数。拷贝构造函数的形参必须为类的对象。构造函数与析构函数(2)析造函数名由类名前加“”组成,且无参数及返回类型。其作用是撤消对象,并回收对象占用的内存空间。(3)构造与析构函数的调用用类说明一个对象时,系统先为其分配内存空间,再调用构造函数对数据成员进行初始化。当对象结束其生命期,系统调用析构函数,回收对象所占用的内存空间。构造函数与析构函数(4)构造函数与对象成员在建立新类时,允许用已存在的类定义对象成员,类的构造函数格式为::(形参):(实参表1),(实参表2),(实参表n)函数体 构造函数的调用顺序是,先调用各对象成员的构造函数,再调用类本身的构造函数。析构函数的调用顺序与构造函数正好相反。4.new与delete运算符 new 运算符用于动态建立对象,用new动态建立对象时,系统先为对象分配内存空间,后调用构造函数对对象初始化。用new运算符动态建立的对象,必须用delete运算符撤消。delete运算符用于撤消动态建立的对象,用delete 运算符撤消动态建立的对象时,系统先调用析构函数,并由析构函数回收对象占用的内存空间。5.this指针用类定义一个对象时,由系统自动建立指向该对象的指针称为this指针。this指针的默认定义格式:*constthis=&对象;例9.1(1)#includestructStudentcharName8;/学生姓名floatPhi,Math,Ave;/物理、数学、平均成绩;voidAverage(Student&s)/计算平均成绩s.Ave=(s.Phi+s.Math)/2;voidDisplay(Students)/输入姓名与成绩couts.Namets.Phits.Mathts.Avestu.Namestu.Phistu.Math;/输入姓名与成绩Average(stu);/计算平均成绩Display(stu);/显示输出学生成绩返回返回例9.2(1)#include#includeclassStudentprivate:charName8;/定义姓名、物理、数学、平均成绩 floatPhi,Math,Ave;/为私有数据成员 public:voidInput(charname,floatphi,floatmath)/定义Input()strcpy(Name,name);Phi=phi;Math=math;例9.2(2)voidAverage(void)/定义Average()Ave=(Phi+Math)/2;voidDisplay(void)/定义Display()coutNametPhitMathtAven;voidOutput(charname,float&phi,float&math,float&ava)strcpy(name,Name);/定义Output()phi=Phi;math=Math;ave=Ave;返回返回例9.4(1)#include#includeclassStudentprivate:charName8;floatPhi,Math,Ave;public:voidInput(charname,floatphi,floatmath);voidAverage(void)Ave=(Phi+Math)/2;voidDisplay(void)coutNametPhitMathtAven;voidOutput(charname,float&phi,float&math,float&ave);例9.4(2)voidStudent:Input(charname,floatphi,floatmath)strcpy(Name,name);Phi=phi;Math=math;void Student:Output(char name,float&phi,float&math,float&ave)strcpy(name,Name);phi=Phi;math=Math;ave=Ave;例9.4(3)voidmain(void)Studentstu1,stu2;charname8;floatphi,math,ave;coutnamephimath;stu1.Input(name,phi,math);stu1.Average();stu1.Output(name,phi,math,ave);coutnametphitmathtaveendl;stu2=stu1;stu2.Display();例9.4(4)程序执行后提示与输出如下:请输入学生姓名、物理与数学成绩:Zhou 90 80 Zhou 90 80 85 Zhou 90 80 85 返回返回例9.5(1)#include#includeclassStudentprivate:charName8;floatPhi,Math,Ave;public:Student(charname,floatphi,floatmath);voidDisplay(void)coutNametPhitMathtAven;voidAverage(void)Ave=(Phi+Math)/2;例9.5(2)Student:Student(charname,floatphi,floatmath)strcpy(Name,name);Phi=phi;Math=math;voidmain(void)Studentstu1(Zhou,90,80);stu1.Average();stu1.Display();程序执行后输出:Zhou908085返回返回例9.6(1)#include#includeclassRectangleprivate:intLeft,Top,Right,Bottom;public:Rectangle(intL,intT,intR,intB)cout调用带参构造函数endl;Left=L;Top=T;Right=R;Bottom=B;Rectangle()cout调用无参构造函数endl;Left=0;Top=0;Right=0;Bottom=0;intArea(void)returnabs(Right-Left)*(Bottom-Top);例9.6(2)voidmain(void)Rectangler1(100,50,200,100);cout矩形r1的面积=r1.Area()endl;Rectangler2;cout矩形r2的面积=r2.Area()endl;程序执行后输出:调用带参构造函数矩形r1的面积=5000调用无参构造函数矩形r2的面积=0返回返回例9.7(1)#include#includeclassRectangleprivate:intLeft,Right,Top,Bottom;public:Rectangle(intL,intR,intT,intB)cout调用带参构造函数endl;Left=L;Right=R;Top=T;Bottom=B;例9.7(2)Rectangle()cout调用无参构造函数endl;Left=0;Top=0;Right=0;Bottom=0;Rectangle(Rectangle&r)cout调用拷贝构造函数endl;Left=r.Left;Right=r.Right;Top=r.Top;Bottom=r.Bottom;intArea(void)returnabs(Right-Left)*(Bottom-Top);例9.7(3)voidmain(void)Rectangler1(100,200,50,100);cout矩形r1的面积=r1.Area()endl;Rectangler2;cout矩形r2的面积=r2.Area()endl;Rectangler3(r1);/调用拷贝构造函数初始化r3cout矩形r3例9.7(4)程序执行后输出:调用带参构造函数矩形r1的面积=5000调用无参构造函数矩形r2的面积=0调用拷贝构造函数矩形r3的面积=5000 返回返回例9.8(1)#include#includeclassRectangleprivate:intLeft,Top,Right,Bottom;public:Rectangle(intL,intT,intR,intB)cout调用带参构造函数endl;Left=L;Top=T;Right=R;Bottom=B;例9.8(2)Rectangle()cout调用无参构造函数endl;Left=0;Top=0;Right=0;Bottom=0;intArea(void)returnabs(Right-Left)*(Bottom-Top);例9.8(3)voidmain(void)Rectangle*pr1=newRectangle(100,50,200,100);cout矩形r1的面积=Area()endl;Rectangle*pr2=newRectangle;cout矩形r2的面积=Area()endl;deletepr1;deletepr2;例9.8(4)程序执行后输出:调用带参构造函数矩形r1的面积=5000调用无参构造函数矩形r2的面积=0返回返回例9.9(1)#include#includeclassRectangleprivate:intLeft,Top,Right,Bottom;public:Rectangle(intL,intT,intR,intB)cout 调用带参构造函数Rectangle(intL,int T,int R,int B)endl;Left=L;Top=T;Right=R;Bottom=B;例9.9(2)Rectangle(intR,intB)cout调用带参构造函数Rectangle(intR,intB)endl;Left=0;Top=0;Right=R;Bottom=B;Rectangle()cout调用了析构函数!endl;intArea(void)returnabs(Right-Left)*(Bottom-Top);voidmain(void)Rectangler1(100,200,50,100);cout矩形r1的面积=r1.Area()endl;例9.9(3)Rectangler2(100,100);cout矩形r2的面积=r2.Area()endl;执行程序后输出:调用带参构造函数Rectangle(intL,intR,intT,intB)矩形r1的面积=5000调用带参构造函数Rectangle(intR,intB)矩形r2的面积=10000调用了析构函数!调用了析构函数!返回返回例9.10(1)#includeclassComplexprivate:floatReal,Image;/定义复数实部与虚部为私有数据成员public:Complex(floatR=0,floatI=0)/定义带参构造函数cout调用带参构造函数!endl;Real=R;Image=I;Complex(Complex&c)/定义拷贝构造函数cout调用拷贝构造函数!endl;Real=c.Real;Image=c.Image;例9.10(2)Complex()cout调用析构函数!endl;voidShow(inti)coutci=Real+Imageiendl;voidmain(void)Complexc1(10,20);c1.Show(1);Complexc2(c1);c2.Show(2);Complexc3;c3.Show(3);例9.10(3)程序执行后输出:调用带参构造函数!c1=10+20i调用拷贝构造函数!c2=10+20i调用带参构造函数!c3=0+0i调用析构函数!调用析构函数!调用析构函数!返回返回例9.11(1)#includeclassComplexpublic:floatReal,Image;public:Complex(floatR,floatI)Real=R;Image=I;cout调用带参构造函数Complex(float,float)endl;Complex()Real=0;Image=0;cout调用无参构造函数Complex()endl;例9.11(2)Complex()cout调用了析构函数!endl;voidShow(inti)/定义显示复数成员函数coutci=Real+ImageiShow(1);Complex*pc2=newComplex;pc2-Show(2);deletepc1;/回收pc1所指对象占用的内存空间deletepc2;/回收pc2所指对象占用的内存空间例9.11(3)程序执行后输出:调用带参构造函数Complex(float,float)c1=10+20i调用无参构造函数Complex()c2=0+0i调用了析构函数!调用了析构函数!返回返回 例9.12(1)#includeclassRectangle/定义描述矩形的类Rectangleprivate:intLength,Width;public:Rectangle(intl,intw)Length=l;Width=w;intArea()returnLength*Width;例9.12(2)classCuboid/定义描述长方体的类Cubiodprivate:intHigh;Rectangler;/用矩形类的对象rpublic:Cuboid(inth,intl,intw):r(l,w)/定义长方体类的构造函数High=h;intVolume()returnHigh*r.Area();例9.12(3)voidmain(void)Cuboidc(10,20,300);/用类Cuboid定义长方体对象c/调用对象c的成员函数Volumn()cout“长方体体积=”c.Volume()endl;程序执行后输出:长方体体积=60000 返回返回 例9.13(1)#includeclassObjprivate:intval;public:Obj()val=0;coutvalt调用Obj缺省的构造函数!endl;Obj(inti)val=i;coutvalt调用Obj的构造函数!endl;例9.13(2)Obj()coutvalt“调用Obj的析构函数!endl;classConprivate:Objone,two;intdata;public:Con()data=0;coutdatat调用Con缺省的构造函数!endl;例9.13(3)Con(inti,intj,intk):two(i+j),one(k)data=i;coutdatat调用Con的构造函数!endl;Con()coutdatat“调用Con的析构函数!endl;voidmain(void)Conc(100,200,400);例9.13(4)执行程序后输出:400调用Obj的构造函数!300调用Obj的构造函数!100调用Con的构造函数!100调用Con的析构函数!300调用Obj的析构函数!400调用Obj的析构函数!返回返回