对象生灭 - 韩山师范学院_第1页
对象生灭 - 韩山师范学院_第2页
对象生灭 - 韩山师范学院_第3页
对象生灭 - 韩山师范学院_第4页
对象生灭 - 韩山师范学院_第5页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

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

文档简介

对象生灭构造函数设计(ConstructorDesign)构造函数重载(ConstructorOverload)类成员初始化(ClassMemberInitializations)构造顺序(ConstructingOrder)拷贝构造函数(CopyConstructors)析构函数(Destructors)对象转型与赋值(Conversion&Assignment)在C++语言中,不仅定义对象和定义变量类同,更进一步说,对象的作用域也和传统程序中变量的作用域类同。对象按作用域不同可划分为以下3种。(1)全局对象:程序运行时,此类对象被创建,且以全0的形式表示对象;程序结束时,此类对象被撤销;(2)局部对象:程序运行到某函数(程序块)时,此类对象被创建,并以随机值表示对象;程序退出该函数(程序块)时,此类对象被撤销;(3)堆对象(动态对象):执行运算符new时,此类对象被创建,执行运算符delete时,此类对象被撤销。//日期类classDate{

intyear,month,day;

public:

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

bool

isLeapYear(inty)const;//判断闰年

friendostream&operator<<(ostream&out,constDate&d);

……

};Dated;//全局对象intmain(){Datee;//局部对象

cout<<d<<e;}在建立一个对象时,常常需要作某些初始化的工作,例如对数据成员赋初值。类对象的初始化任务,自然就落在了类的成员函数身上,因为它们可以访问保护和私有数据成员。要求建立对象的同时,自动调用这个成员函数,省去人为调用的麻烦。这个函数就是构造函数.§构造函数设计构造函数主要是两个功能:为对象开辟空间,为对象中的数据成员赋初值。构造函数的名字一定和类的名字相同。在该类的对象创建时,自动被调用。构造函数不应返回值,也不应是void类型函数。如果没有定义构造函数,编译系统会自动生成一个默认的构造函数。该构造函数不带参数,函数体是空的,不作任何操作。构造函数可以带参数或没带参数、可以设置默认参数,可以重载。classDate{

intyear,month,day;public:

Date(inty=2000,intm=1,intd=1);//在声明时设置默认参数

……};Date::Date(inty,intm,intd){year=y,month=m,day=d;}//------------------------------------------------------------------------------intmain(){Dated(2003,12,6);Datee(2002);//默认两个参数

Datef(2002,12);//默认一个参数

Dateg;//默认三个参数

cout<<d<<e<<f<<g;}在一个类中可以定义多个构造函数,以便对类对象提供不同的初始化的方法,供用户选用。这些构造函数具有相同的名字,而参数的个数或参数的类型不相同。这称为构造函数的重载。§构造函数的重载classDate{

intyear,month,day;public:

Date(inty=2000,intm=1,intd=1);//设置默认参数

Date(conststring&s);//构造函数重载

……};//-------------------------------------------------------------------Date::Date(conststring&s){year=atoi(s.substr(0,4).c_str());month=atoi(s.substr(5,2).c_str());day=atoi(s.substr(8,2).c_str());}//------------------------------------Date::Date(inty,intm,intd){year=y,month=m,day=d;}//----------------------------------------------------------------------intmain(){Dated("2006-12-26");Datee(2000,12,25);Datef(2001,10);Dateg(2002);Dateh;//无参对象定义

Date("2006-12-26");//一次性对象}注:如果手工定义了构造函数,则系统不再提供默认的构造函数classDate{

intyear,month,day;public:

Date(inty,intm,intd);

Date(conststring&s);……};intmain(){Dated("2006-12-26");Datee(2000,12,25);Datef;//error}§类成员初始化(ClassMemberInitializations)数据成员的空间分配是在构造函数被调用和其过程被执行时完成,如果在类中有对象成员时,那么刹那间便是调用对象所在类的构造函数,以创建对象空间的时机。classStudentID{

intvalue;public:

StudentID(){//无参构造函数

staticint

nextStudentID=0;value=++nextStudentID;

cout<<"StudentId:"<<value<<"\n";}};//-----------------------------------classStudent{stringname;

StudentIDid;//对象成员public:

Student(stringn="noName"){

cout<<"Student:"+n+"\n";name=n;}};//-----------------------------------intmain(){Students("Randy");}运行结果:

StudentId:1

Student:Randy说明先成员构造,后自身构造.成员构造不见显式调用,而是默认调用StudentID类的无参构造函数.Q:若要把参数传递给对象成员的有参构造函数,那么类该如何操作?classStudentID{

intvalue;public:

StudentID(intid=0){value=id;

cout<<"StudentId"<<value<<"\n";}};//-----------------------------------classStudent{stringname;

StudentIDid;public:

Student(stringn="noName",int

ssID=0){

cout<<"Student"+n+"\n";name=n;

StudentID

id(ssID);//id是局部对象}};//-----------------------------------intmain(){Students("Randy",58);}运行结果:

StudentId:0

Student:RandyStudentId:58classStudentID{

intvalue;public:

StudentID(intid=0){value=id;

cout<<"StudentId"<<value<<"\n";}};//-----------------------------------classStudent{stringname;

StudentIDid;public:

Student(stringn="noName",int

ssID=0):name(n),id(ssID){

cout<<"Student"+n+"\n";}};//-----------------------------------intmain(){Students("Randy",98);Studentt("Jenny");}其运行结果为:

StudentId:98

Student:Randy

StudentId:0

Student:JennyC++定义了一个新的方式,在类的构造函数定义中,使其带有成员初始化列表的形式,从而对类中的成员进行初始化。构造函数(参数列表):成员初始化列表

{…………}成员1(参数表1),

成员2(参数表2),...,成员n(参数表n)常量成员和引用成员在程序运行期间不能改变,但通过上面成员初始化方式可以实现#include<iostream.h>classDate{private:

constintyear;//常量成员

constintmonth;constintday;public:…….

constint&r;//常量引用};Date::Date(inty,intm,intd):year(y),month(m),day(d),r(year){}§构造顺序在一个大程序中,各种作用域的对象很多,有些对象包含在别的对象里面,有些对象早在主函数开始运行之前就已经建立。创建对象的唯一途径是调用构造函数。构造函数是一段程序,所以构造对象的先后顺序不同,直接影响程序执行的先后顺序,导致不同的运行结果。C++给构造对象的顺序作了专门的规定。

1.局部和静态对象根据运行中定义对象的顺序来决定对象创建的顺序。

classA{public:A(){cout<<"A->";}};//----------------------------classB{public:B(){cout<<"B->";}};//----------------------------classC{public:C(){cout<<"C->";}};//----------------------------voidfunc(){

cout<<"\nfunc:";

Aa;//局部对象

staticBb;//静态对象

Cc;//局部对象}//--------------------------------intmain(){

cout<<"main:";

for(inti=1;i<=2;++i){

for(intj=1;j<=2;++j)

if(i==2)Cc;elseAa;Bb;}

func();func();}运行结果:main:A->A->B->C->C->B->func:A->B->C->func:A->C->包含局部对象的函数或程序块每被调用(执行)一次,局部对象就被构造一次。静态对象只被构造一次

静态对象和静态变量一样,文件作用域的静态对象在主函数开始运行前全部构造完毕。块作用域中的静态对象,则在首次进入到定义该静态对象的函数时,进行构造。2.全局对象

和全局变量一样,所有全局对象在主函数开始运行之前,全部已被构造。这会给调试带来问题。当要开始调试时,所有全局对象的构造函数都已被执行,如果它们中的一个有致命错误,那么你可能永远也得不到控制权。这种情况下,该程序在它开始执行之前就死机了。有两种方法可以解决这个问题:一是将全局对象先作为局部对象来调试;二是在所有怀疑有错的构造函数的开头,增加输出语句,这样在程序开始调试时,你可以看到来自这些对象的输出信息。

单个文件的程序中,全局对象按定义的顺序进行构造。

多个文件组成的,这些文件被分别编译、连接。因为编译器不能控制文件的连接顺序,所以它不能决定不同文件中全局对象之间的构造顺序。

例如,下面的代码是个多文件程序结构,创建了两个全局对象://=====================================//student.h//=====================================#include<iostream>usingnamespacestd;//-------------------------------------classStudent{

constintid;public:

Student(int

d):id(d){

cout<<"student\n";}voidprint(){cout<<id<<"\n";}};//-----------------------------------classTutor{Students;public:

Tutor(Student&st):s(st){cout<<"tutor\n";s.print();}};//-----------------------------------//========================//f09092.cpp//========================#include"student.h"Studentra(18);//全局对象//========================//f0909.cpp//========================#include"student.h"externStudentra;//-----------------------------------------Tutorje(ra);//全局对象intmain(){}运行结果:tutor0student为了避免编译器实现中的不确定问题,应尽量不要设置全局对象.3.成员对象classA{public:

A(intx){cout<<"A:"<<x<<"->";}};//-----------------------------------classB{public:

B(intx){cout<<"B:"<<x<<"->";}};//-----------------------------------classC{

Aa;Bb;public:

C(int

x,int

y):b(x),a(y){cout<<"C\n";}};//-----------------------------------intmain(){Cc(15,9);}以其在类中声明的顺序构造运行结果:A:9->B:15->C例:#include<iostream>classA{public:

A(intj):age(j),num(age+1){cout<<“age:”<<age<“,num:”<<num<<endl;}protected:

intnum;

intage;};voidmain(){Asa(15);}

运行结果:age:15,num:2当构造函数的参数为自身类的对象引用时,这个构造函数称为拷贝构造函数。拷贝构造函数的功能是用一个已有对象初始化一个正在建立的同类对象,例如:

Students1("Jenny");

Students2=sl;//用s1的值去初始化s2

或Students2(sl);§拷贝构造函数调用拷贝构造函数对象作为函数参数传递时,也要涉及对象的拷贝,例如:voidfn(Studentfs){ //…}voidmain(){ Studentms; fn(ms);}拷贝构造函数具有以下特点:

1、是一种特殊的构造函数,用于将一个已知对象的数据成员的值拷贝给正在创建的另一个对象。

2、只有一个参数,且必需是对自身类对象的常量引用:P316

3、定义拷贝构造函数的格式:

class类名{public:

类名(形参);//构造函数类名(const

类名&对象名);//拷贝构造函数

...};一、默认的拷贝构造函数如果不定义拷贝构造函数,系统自动为类提供一个默认的拷贝构造函数(逐一拷贝数据成员)。//例:point.cpp二、自定义拷贝构造函数例:拷贝构造函数的使用classpoint{public:

point(floatx1=0,floaty1=0);//带默认参数构造函数

point(constpoint&p);//拷贝构造函数

voidshow();//打印显示点

private:floatx;//点的横坐标

floaty;//点的纵坐标};//类定义结束point::point(floatx1,floaty1){x=x1;y=y1;}point::point(constpoint&p)//拷贝构造函数{x=p.x;y=p.y;}voidpoint::show()//显示点{cout<<x<<","<<y<<endl;}voidmain(){pointp1;pointp2(2,2);//调用参数构造函数

pointp3(p2);//调用拷贝构造函数

p1.show();p2.show();p3.show();}运行结果:0,0

2,2

2,2

注意:一般情况下,不必定义拷贝构造函数。但是,如果构造函数中存在动态分配,则必须定义拷贝构造函数//f0912.cppclassPerson{public: Person(char*pN=“noName”){//构造函数

cout<<"Constructing"<<pN<<endl;

pName=newchar[strlen(pN)+1];//分配堆内存

if(pName!=0) strcpy(pName,pN); } ~Person(){

cout<<"Destructing"<<pName<<endl;

delete[]

pName; }protected: char*pName;};new运算符按要求分配堆内存,如果成功,则返回指向该内存起始地址的指针;如果不成功时,new运算符返回空指针NULL。voidmain(){ Personp1("Randy"); Personp2=p1;//即Personp2(p1);调用默认拷贝构造函数}执行结果出错pNamep1堆pNamep1堆pNamep2默认拷贝构造函数仅仅拷贝对象本体//定义拷贝构造函数来解决上述问题classPerson{public: Person(char*pN=“noName”) {

cout<<"Constructing"<<pN<<endl;

pName=newchar[strlen(pN)+1];

if(pName!=0) strcpy(pName,pN); }

Person(Person&p) {

cout<<"Copying"<<p.pName<<"intoitsownblock\n";

pName=newchar[strlen(p.pName)+1];

if(pName!=0)strcpy(pName,p.pName); }pNamep1堆pNamep1堆pNamep2堆在默认拷贝构造函数中,拷贝的策略是逐个成员依次拷贝。但是,一个类可能会拥有资源,当其构造函数分配了一个资源(例如堆内存)的时候,会发生什么呢?如果拷贝构造函数简单地制作了一个该资源的拷贝,而不对它本身分配,就得面临一个麻烦的局面:两个对象都拥有同一个资源。当对象析构时,该资源将经历两次资源释放。发生以下三种情况拷贝构造函数被调用。--用类的对象去初始化该类的另一个对象时。--对象作为函数的实参传递给函数的形参时。--函数返回值是类的对象,函数调用返回时。一个类可能在构造函数里分配资源,这些资源需要在对象不复存在以前被释放。例如,如果构造函数打开了一个文件,文件就需要被关闭。或者,如果构造函数从堆中分配了内存,这块内存在对象消失之前必须被释放。C++提供了析构函数

(destructor)自动完成这些清理工作,不必调用其他成员函数。析构函数也是一个特殊的成员函数,它没有返回类型,没有参数,也没有重载。只是在类对象生命期结束的时候,由系统自动调用。一个类可以有多个构造函数,但只能有一个析构函数。如果用户没有定义析构函数,C++编译系统会自动生成一个析构函数,

§析构函数当对象的生命期结束时,会自动执行析构函数。具体地说如果出现以下几种情况,程序就会执行析构函数:

①如果在一个函数中定义了一个对象(它是自动局部对象),当这个函数被调用结束时,对象应该释放,在对象释放前自动执行析构函数。②如果定义了一个全局对象,则在程序的流程离开其作用域时(如main函数结束或调用exit函数)时,调用该全局对象的析构函数。③如果用new运算符动态地建立了一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。它撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新对象使用。在一般情况下,调用析构函数的次序正好与调用构造函数的次序相反:最先被调用的构造函数,其对应的(同一对象中的)析构函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用。如图classA{public:A(){cout<<"A->";}~A(){cout<<"<-~A";}};//-------------------------------classB{public:B(){cout<<"B->";}

~B(){cout<<"<-~B";}};//------------------------------classC{public:C(){cout<<"C->";}~C(){cout<<"<-~C";}};//------------------------------voidfunc(){

cout<<"\nfunc:";Aa;

cout<<"ok->";staticBb;Cc;}//----------------------------intmain(){

cout<<"main:";

for(inti=1;i<=2;++i){

for(intj=1;j<=2;++j)

if(i==2)Cc;elseAa;Bb;

}

func();func();}//=================运行结果:main:A-><-~AA-><-~AB-><-~BC-><-~CC-><-~CB-><-~Bfunc:A->ok->B->C-><-~C<-~Afunc:A->ok->C-><-~C<-~A<-~B析构函数在对象的生命期结束时被自动调用静态对象在程序运行结束时被自动调用转型与赋值(Conversion&Assignment)对象转型:类类型,其自动转换的功能必须编程实现,定义含一个参数的构造函数。

classStudent{stringname;public:

Student(conststring&s):name(s){}};//-----------------------------------voidfn(Student&s){cout<<"ok\n";}//-------------------------------------intmain(){

stringt="jenny";fn(t);//参数为string,却能匹配Student类型}//=====================用构造函数实现从一种类型转成另一种类型。对象转型的规则:只会尝试含有一个参数的构造函数如果有二义性,则会放弃尝试推导是一次性的,不允许多步推导//f0915.cppclassStudent{stringname;public:

Student(conststring&s):name(s){}};//-----------------------------------voidfn(Student&s){cout<<"ok\n";}//-------------------------------------

温馨提示

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

评论

0/150

提交评论