第五章类和简单对象概述类定义对象的定义对象的初始化成员函数的特性静态成员友员对象的生存期5.1概述前面介绍的程序设计方法是把描述某一事务的数据与对数据进行操作的函数分开这种方法的缺陷是:当描述事务的数据结构发生变化时,处理这些数据结构的函数必须重新设计和调试,而再调试函数时,又有可能修改了不应修改的数据。编写大的程序时,这给调试程序和程序的维护都带来很大的问题。由于把函数与要处理的数据分开,对数据结构和函数的任何不适当的修改都可能导致整个程序不能正确执行。如:{inta(5),b(7);intc=add(a,b);coutcendl;}intadd(inta,intb){returna+b;}inta[]={1,2,3},b[]={4,5,6}为了克服以上的缺点,采用OOP程序设计方法,OOP的基本要求是将描述某一事物的数据与所有处理这些数据的函数都封装成一个整体,只有通过这一整体中的函数才能修改这一封装载一起的数据。这种将数据与处理这些数据的函数封装成一个整体,就构成一个类。ClassAdd{public:voidsetab(intx,inty){a=x;b=y;}intadd(){returna+b;}private:inta,b;};voidmain(){Addp;p.setab(3,5)intc=p.add();coutcendl;}classRec{public:voidsetab(intx,inty){a=x;b=y;}doublearea(){returnA=a*b;}doubleper(){returnP=2*(a+b);}private:inta,b,P,A;};voidmain(){Recp;p.setab(3,5)doublea=p.area();coutaendl;doublep=p.per();coutpendl;}classRec{public:voidsetab(intx,inty){a=x;b=y;}voidarea(){A=a*b;}voidper(){P=2*(a+b);}voidprint(){coutAendl;coutPendl;}private:inta,b,P,A;};voidmain(){Recp;p.setab(3,5)p.area();p.per();p.print();}类中的数据在类的外部是不可见的,外部只能通过公共接口(类中的函数)与类中的数据发生联系,从而可以显著提高程序模块的独立性和可维护性。类是一种数据类型,是对事物抽象描述,类在程序运行中,被用作样板来建立对象。对象是类的实例,一个对象占用计算机内存中的一个区域;对象之间保持相对独立。即实现了对象的封装性5.2类定义只有该类的成员函数或友元函数才可访问不仅可被该类的成员函数访问而且还可被体外的程序访问格式:class类名{public:成员表1protected:成员表2private:成员表3};classTDate{public:voidSetDate(inty,intm,intd);intIsleapYear();voidPrint();private:intyear,month,day;};公有成员函数都可访问私有成员数据Ex:定义一个日期类//说明部分://实现部分:voidTdate::SetDate(inty,intm,intd){year=y;month=m;day=d;}IntTdate::IsleapYear(){return(year%4==0&&year%100!=0)||(year%400==0);}VoidTdate::Print(){coutyear.month.dayendl;}::——作用域运算符,标识成员函数属于哪个类。在类中定义成员函数:classTDate{public:voidSetDate(inty,intm,intd){year=y;month=m;day=d;}intIsleapYear(){return(year%4==0&&year%100!=0)||(year%400==0);}voidPrint(){coutyear.month.“dayendl;}private:intyear,month,day;};注:不能对普通数据成员初始化。类中的任何成员不能用auto,extern和register存储类说明符进行修饰私有成员,不能访问5.3对象的定义一个类的普通成员数据,在不同的对象中占用不同的空间;但成员函数不管在哪个实例中调用,都占用同一空间。类的使用:voidmain(){TDatedate1,date2,*p,date[5];p=&date2;date1.SetDate(1999,6,8);p-SetDate(2000,3,8);//(*p).SetDate(2000,3,8)date1.day=7;}通过成员函数给对象的成员数据赋值。L5_1.dsw×classTpoint{public:voidSetpoint(intx,inty){X=x;Y=y;}intXcoord(){returnX;}intYcoord(){returnY;}voidMove(int,int);private:intX,Y;};VoidTpoint::Move(intsetx,intsety){X+=setx;Y+=sety;}例:定义一个描述二维平面上坐标的类l5_2_1注:形参变量不能与成员变量重名,若重名只是把形参的值赋给了形参变量,而不会赋值给成员变量。5.4.1构造函数可用一特殊成员函数(构造函数)给对象初始化,每当创建对象时系统自动调用此函数、名字与类名相同、无类型(无需void)、可以重载。注:用该类定义对象时,构造函数必须是公有的,若定义的类仅用于派生类时,该类的构造函数可定义保护的。5.4对象的初始化inta(7);TDatedate(1998,4,5)?classTDate{private:intyear,month,day;public:TDate(inty,intm,intd){year=y;month=m;day=d;}voidPrint(){coutyearmonthdayendl;};构造函数voidmain(){TDateT(2000,5,9);T.Print();}说明P1441、说明声明了一个Tdate对象T2、实例化为T分配空间3、初始化调用构造函数5.4.2析构函数名字与类名相同(类名前加~),无参数,不能重载,无类型(无需void)、公有成员函数.对象生命期结束时系统自动调用classTDate{private:intyear,month,day;public:TDate(inty,intm,intd){year=y;month=m;day=d;}~TDate(){cout“Destructor“end;}voidPrint(){coutyearmonthdayendl;}};voidmain(){TDatet(2000,5,9);t.Print();}调用析构函数L5_3.dsw×classTDate{private:intyear,month,day;public:TDate(inty,intm,intd){year=y;month=m;day=d;cout“cons”endl;}~TDate(){cout“Des“end;}voidPrint(){coutyearmonthdayendl;}};voidmain(){TDatet1(2000,5,9);TDatet2(2005,5,9);t1.Print();t2.Print();}5.4.3缺省构造函数和缺省析构函数classTDate{private:intyear,month,day;public:voidPrint(){coutyearmonthdayendl;};voidmain(){TDatetoday;today.Print();}调用缺省析构函数调用缺省构造函数缺省构造函数:类名::类名(){}说明:1编译器缺省的构造函数并不对所产生对象的数据成员赋初值,即对象的数据成员的值是不确定的。2在定义类时,若定义了类的构造函数,则编译器不产生缺省的构造函数。3在类中,若定义了没有参数的构造函数或各参数均有缺省值的构造函数也称为缺省的构造函数,缺省的构造函数只能有一个。4要对对象的数据成员进行初始化时,必须定义构造函数。B5_1圆柱体.cpp缺省析构函数类名::~类名(){}在撤消对象时,若不做任何结束工作,可以不显式地定义析构函数。例:1、编写一个程序,采用一个类求矩形的面积与周长。2、编写一个程序,采用一个类求圆柱体的体积。3、一维数组元素之和。5.4.4拷贝构造函数Inta(8);intb=a;intc(b),d;TPointP1(5,7);TPointP2(P1),p3=p2;格式:类名::类名(类名&引用名){……}由已知的对象初始化另一对象时调用Ql7_6.cpp×L5_5.cpp×隐含拷贝构造函数TPoint::TPoint(TPoint&p){X=p.X;Y=p.Y;}假定类中有两个普通数据成员.产生一个临时对象,赋值完后,撤消临时对象调用析构函数。5.4.5对象的其它赋值方法Tpointa,b(3,5);调用构造函数Tpointc=b;或c(b);调用拷贝构造函数a=b;通过类型转换赋值a=Tpoint(100,400);a=8;构造函数只有一个形参时产生一个临时的对象(无名对象),调用类Tpoint的构造函数,完成赋值后,立即撤消该临时的对象调用析构函数。5.5成员函数的特性5.5.1内联函数和外联函数内联函数外联函数加inline转换为内联函数定义在类体内的成员函数InlineVoidTdate::Print(){coutyear.month.dayendl;}说明在体内,定义在类体外的成员函数L5_65.5.2重载性成员函数,构造函数可重载;析构函数不能重载.Ex5.75.5.3设置参数的缺省值当成员函数,构造函数的形参有默认值时实参的个数可小于形参5.6静态成员5.6.1静态数据成员要实现同一类的不同对象之间的数据共享,可用静态数据成员.静态数据成员定义在类体内,定义时前面加关键字static.L5_8ClassTtest{//……private:inta,b;staticintc;};intTtest::c=500;//intTtest::c;值为零必须初始化,不加static,为C分配存储空间特点:1静态成员是属于类的,而不是属于某个对象的。静态成员是所有对象共享的,使用它可以节约内存。2静态数据成员定义在类体内,初始化在类体外。初始化后才可使用。L5_103定义静态数据成员像一般数据成员一样,应该给出数据类型和访问权限。4公有静态成员访问格式:(1)类名::静态成员名(2)对象名.静态成员名b5_25.6.2静态成员函数只可以引用属于该类的静态数据成员或静态成员函数。通过对象引用非静态成员公有静态成员函数在程序中的调用voidmain(){Ap(4);p.fun1();A::fun2(p);}ClassA{public:A(intx){a=x;}voidfun1(){couta“,”bendl;}staticvoidfun2(Am){coutm.aendl;coutbendl;private:inta;staticintb;};intA::b=5;p.fun2(p)Ql7_12×特点:1定义静态成员函数的目的是为了对类进行处理,通常是为了处理类中的静态数据成员。因此,在没有对象存在的情况下,也可调用静态成员函数。2由于静态成员函数是用于处理类的,因此不