第3章怎样使用类和对象1第3章怎样使用类和对象3.1利用构造函数对类对象进行初始化3.2利用析构函数进行清理工作3.3调用构造函数和析构函数的顺序3.4对象数组3.5对象指针3.6共用数据的保护3.7对象的动态建立和释放3.8对象的赋值和复制3.9静态成员3.10友元3.11类模板(自学)23.1利用构造函数对类对象进行初始化一、对象的初始化•在建立一个对象时,常常需要作某些初始化的工作,即对数据成员赋初值。1.若一个类中所有的数据成员都是public的,则可以在定义对象时对数据成员进行初始化。classTime{public://声明为公有成员inthour;intminute;intsec;};Timet1={14,56,30};//定义t1的同时初始化3注意:如果类中有private或protected的成员,不能在定义对象时对数据成员进行初始化。可通过调用公有的成员函数来对数据成员赋初值。2.C++提供了构造函数(constructor)来处理对象的初始化。4二、用构造函数实现数据成员的初始化•构造函数是一种特殊的成员函数,用于对对象的数据成员进行初始化。•构造函数的名字必须与类名同名。•构造函数没有返回值,因此无函数返回值类型。•构造函数可以带或不带参数,可以重载,也可含有默认参数。•构造函数的一般格式为:类名(类型1形参1,类型2形参2,…){……//对数据成员进行初始化}5•若想要定义类的对象,构造函数必须是公有的(public)成员函数。•用户不能通过对象名来调用构造函数,在建立对象时系统自动调用,实参在定义对象时给出。•定义对象的一般格式:类名对象名(实参1,实参2,…);//构造函数有参类名对象名;//构造函数无参•如果用户没有定义构造函数,则C++系统会自动生成一个缺省的构造函数:类名(){}//无参、空函数体系统根据实参自动调用相应的构造函数6#includeiostream.hclassA{floatx,y;public:A(floata,floatb){x=a;y=b;}//构造函数,初始化对象voidSet(floata,floatb){x=a;y=b;}Print(){coutx=x'\t'y=yendl;}};voidmain(){Aa1;//用户未定义构造函数,则调用系统自动生成的a1.Set(10.0,20.0);//调用成员函数为对象的成员赋值a1.Print();}A(){}//缺省的构造函数只要显式定义了一个类的构造函数,则编译器就不产生缺省的构造函数Aa1(10.0,20.0);//定义时调用构造函数初始化a1.Print();7例3.2有两个长方柱,长length、宽width、高height分别为:(1)12,20,25;(2)10,14,20。求它们的体积。编一个基于对象的程序,在类中用带参数的构造函数。#includeiostream.hclassBox{public:Box(inth,intw,intlen);//声明带参数的构造函数intvolume();//声明计算体积的函数private:intheight;//高intwidth;//宽intlength;//长};8Box::Box(inth,intw,intlen)//在类外定义带参数的构造函数{height=h;width=w;length=len;}intBox::volume()//定义计算体积的函数{return(height*width*length);}voidmain(){Boxbox1(12,20,25);//建立对象box1coutThevolumeofbox1isbox1.volume()endl;Boxbox2(10,14,20);//建立对象box2coutThevolumeofbox2isbox2.volume()endl;}9三、用参数初始化表对数据成员初始化•不在构造函数的函数体内对数据成员初始化,而是在构造函数的函数首部通过参数初始化表来实现对数据成员的初始化。Box::Box(inth,intw,intlen):height(h),width(w),length(len){}用参数初始化表更方便、简练,尤其当需要初始化的数据成员较多时更显其优越性。Box::Box(inth,intw,intlen)//带参数的构造函数{height=h;width=w;length=len;}10四、构造函数的重载•构造函数的重载:在一个类中可以定义多个构造函数,以便对类对象提供不同的初始化的方法。这些构造函数具有名字相同,而参数的个数、类型或顺序不同。•构造函数中参数可以指定为某些默认值(缺省值)。如果用户不指定实参值,编译系统就使形参取默认值。11#includeiostream.hclassA{floatx,y;public:A(floata,floatb=10);//通常在声明时指定默认值A();voidPrint(void){coutx'\t'yendl;}};A::A(floata,floatb)//不可重复指定默认值{x=a;y=b;}A::A(){x=0;y=0;}voidmain(){Aa1,a2(20.0),a3(3.0,7.0);a1.Print();a2.Print();a3.Print();}带缺省参数的构造函数每一个对象必须要有相应的唯一的构造函数!Aa4(1,2,3);//错误!定义对象a1时不能写成Aa1();//此为声明函数00a1xy2010a237a3//无参构造函数(默认构造函数)123.2利用析构函数进行清理工作•析构函数也是一个特殊的成员函数,它的作用与构造函数相反,在对象的生命期结束时,由系统自动调用析构函数,撤消一个对象(完成析构函数中指定的操作后,释放对象的空间)。•析构函数的名字是类名前加“~”符号。析构函数不能带参数,无返回值,不指定函数类型,不允许重载。•析构函数的一般格式为:~类名(){……}//可以在类内或类外定义•如果用户没有定义析构函数,则C++系统会自动生成一个析构函数:~类名(){}//空函数体13例3.5包含构造函数和析构函数的C++程序。#includestring#includeiostreamusingnamespacestd;classStudent//声明Student类{public:Student(intn,stringnam,chars)//定义构造函数{num=n;name=nam;sex=s;coutConstructorcalled.endl;//输出有关信息}~Student()//定义析构函数{coutDestructorcalled.endl;}//输出有关信息voiddisplay()//定义成员函数{coutnum:numendl;coutname:nameendl;coutsex:sexendlendl;}14private:intnum;//学号stringname;//姓名charsex;//性别};//endofclassStudentintmain(){Studentstud1(10010,Wang_li,'f');//建立对象stud1stud1.display();//输出学生stud1的数据Studentstud2(10011,Zhang_fun,'m');//定义对象stud2stud2.display();//输出学生stud2的数据return0;}15构造函数析构函数功能对数据成员初始化,由系统自动调用对象释放前的操作,由系统自动调用名字类名()~类名()特点无函数类型可有参数可以重载无函数类型不可有参数不可重载163.3调用构造函数和析构函数的顺序•在使用构造函数和析构函数时,需要特别注意对它们的调用时间和调用顺序。17调用构造函数调用析构函数全局对象静态局部对象局部自动对象动态对象表1构造函数、析构函数的调用时间表在本文件模块中的所有函数(main函数)执行之前调用main函数执行完或调用exit函数时第一次函数被调用,执行到定义对象的地方时main函数执行完或调用exit函数时每当函数被调用,执行到定义对象的地方时在退出对象的作用域时用new新建对象时用delete回收对象时18析构顺序:•对于同一存储类别的对象而言,调用析构函数的次序正好与调用构造函数的次序相反:先构造的后析构,后构造的先析构。相当于一个栈,先进后出。•对于不同存储类别的对象而言,先析构局部自动对象,然后是静态局部对象,然后是全局对象。19#includeiostream.hclassA{floatx;public:A(floata){x=a;cout调用非缺省的构造函数:xendl;}A(){x=0;cout调用缺省的构造函数:xendl;}~A(){cout调用析构函数:xendl;}};voidmain(void){Aa1;Aa2(3.0);cout退出主函数\n;}运行结果:调用缺省的构造函数:0调用非缺省的构造函数:3退出主函数调用析构函数:3调用析构函数:020#includeiostream.hclassA{floatx;public:A(floata){x=a;cout调用非缺省的构造函数:xendl;}A(){x=0;cout调用缺省的构造函数:xendl;}~A(){cout调用析构函数:xendl;}};voidmain(void){Aa1;{Aa2(3.0);}//复合语句中的对象cout退出主函数\n;}运行结果:调用缺省的构造函数:0调用非缺省的构造函数:3调用析构函数:3退出主函数调用析构函数:021#includeiostream.hclassA{intx;public:A(inta=0){x=a;cout调用构造函数:xendl;}~A(){cout调用析构函数:xendl;}};Aa0(60);//定义全局对象voidf(){cout进入f()函数\n;Aa1(40);//定义自动局部对象staticAa2(20);//定义静态局部对象}voidmain(){cout进入main函数\n;Aa3;//定义自动局部对象f();}先析构局部自动对象,然后是静态局部对象,然后是全局对象运行结果:调用构造函数:60进入main函数调用构造函数:0进入f()函数调用构造函数:40调用构造函数:20调用析构函数:40调用析构函数:0调用析构函数:20调用析构函数:60223.4对象数组•对象数组的每一个元素都是同类的对象。Boxa[2];•在定义对象数组时可进行初始化:在花括号中分别写出构造函数并指定实参。Boxa[2]={//定义对象数组Box(10,12,15),//调用构造函数Box,初始化a[0]Box(15,18,20)//调用构造函数Box,初始化a[1]};23classBox{public://声明有默认参数的构造函数,用参数初始化表初始化Box(inth=10,intw=12,intlen=15):height(h),width(w),length(len){coutConstructor:heightendl;}intvolume(){return(height*width*length);}//计算体积~Box(){coutDestructor:heightendl;};private:intheight,width,length;//高、宽、长};voidmain(){Boxa[2]={Box(),Box(18,20)};coutvolumeofa[0]isa[0].volume()endl;coutvolumeofa[1]is