资源预览内容
第1页 / 共50页
第2页 / 共50页
第3页 / 共50页
第4页 / 共50页
第5页 / 共50页
第6页 / 共50页
第7页 / 共50页
第8页 / 共50页
第9页 / 共50页
第10页 / 共50页
亲,该文档总共50页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第二部分面向对象程序设计第二部分面向对象程序设计第十一章第十一章构造函数和析构函数构造函数和析构函数1111111111.1 11.1 11.1 11.1 类与对象类与对象类与对象类与对象第十一章第十一章 目目 录录11.3 11.3 11.3 11.3 析构函数析构函数析构函数析构函数11.2 11.2 11.2 11.2 构造函数构造函数构造函数构造函数11.4 11.4 11.4 11.4 带参数的构造函数带参数的构造函数带参数的构造函数带参数的构造函数11.5 11.5 11.5 11.5 重载构造函数重载构造函数重载构造函数重载构造函数11.6 11.6 11.6 11.6 缺省构造函数缺省构造函数缺省构造函数缺省构造函数第十一章小结第十一章小结第十一章小结第十一章小结11.7 11.7 11.7 11.7 拷贝构造函数拷贝构造函数拷贝构造函数拷贝构造函数11.8 11.8 11.8 11.8 拷贝构造函数的其他用处拷贝构造函数的其他用处拷贝构造函数的其他用处拷贝构造函数的其他用处 C+ 中中构造函数和析构函数是类的特殊成员函数。构造函数和析构函数是类的特殊成员函数。 构造函数用于创建类对象,初始化其成员。构造函数用于创建类对象,初始化其成员。 析构函数用于撤销类对象。析构函数用于撤销类对象。 本章介绍构造函数、析构函数、缺省构造函数、拷贝构本章介绍构造函数、析构函数、缺省构造函数、拷贝构造函数等相关内容。造函数等相关内容。第十一章第十一章 构造函数和析构函数构造函数和析构函数 一个类描述一类事物,描述这些事物所应具有的属性。一个类描述一类事物,描述这些事物所应具有的属性。 对象是类的一个实例,它具有确定的属性。对象是类的一个实例,它具有确定的属性。 如学生类与某学生对象。如学生类与某学生对象。 类的名字只有一个,但由该类创建的对象可以任意多个。类的名字只有一个,但由该类创建的对象可以任意多个。 属于不同类的对象可以在不同时刻、不同环境分别创建或属于不同类的对象可以在不同时刻、不同环境分别创建或撤销。撤销。 与定义变量相同,可定义具有不同存储属性的各类对象。与定义变量相同,可定义具有不同存储属性的各类对象。定义对象时,定义对象时,C+ 编译器为其分配存储空间编译器为其分配存储空间(如果需要如果需要)。11.1 类与对象类与对象例如:下面程序定义了两个类,创建了全局对象和局部对象,例如:下面程序定义了两个类,创建了全局对象和局部对象,静态对象和堆对象。静态对象和堆对象。 class Desk /define class Desk public: int weight; int high; int width; int length; ; class Stool /define class Stool public: int weight; int high; int width; int length;Desk da; /globle object daStool sa; /globle object savoid fn() static Stool ss; /local static object Desk da; /local object /void main() Stool bs; /local object Desk *pd=new Desk; /heap object Desk nd50; /local array of object / delete pd; /clean up object and release resources 前面已经介绍过变量定义时若未显式初始化,全局变量和前面已经介绍过变量定义时若未显式初始化,全局变量和静态变量在定义时初值为静态变量在定义时初值为0,局部变量在定义时初值为随机数。,局部变量在定义时初值为随机数。 与定义变量不同,一旦建立一个对象,对象通常都需要有与定义变量不同,一旦建立一个对象,对象通常都需要有一个有意义的初值。一个有意义的初值。 C+ 建立和初始化对象的过程专门由该类构造函数完成。建立和初始化对象的过程专门由该类构造函数完成。 对象建立时,调用该构造函数,给对象分配存储空间并进对象建立时,调用该构造函数,给对象分配存储空间并进行初始化。行初始化。 当对象撤销时,调用析构函数作善后处理。当对象撤销时,调用析构函数作善后处理。11.2 构造函数构造函数 类创建对象时需要对对象初始化,但初始化任务,只有由类创建对象时需要对对象初始化,但初始化任务,只有由成员函数完成,因此,在类中必须定义一个具有初始化功能的成员函数完成,因此,在类中必须定义一个具有初始化功能的成员函数。成员函数。 每当创建一个对象时,就调用这个成员函数,实现初始化。每当创建一个对象时,就调用这个成员函数,实现初始化。例如:例如: class Student public: void init() semeshours=100; gpa=3.5; / protected: int semeshours; int gpa; ; void fn() Student s; s.init(); /调用类的初始化函数调用类的初始化函数 / 这种将初始化工作交由初始化成员函数完成的方式使系统这种将初始化工作交由初始化成员函数完成的方式使系统多了一道处理过程,增加了书写代码,实现的机制并不理想。多了一道处理过程,增加了书写代码,实现的机制并不理想。 另一种方法是建立对象的同时,自动调用构造函数,省去另一种方法是建立对象的同时,自动调用构造函数,省去上述麻烦,使定义类对象时包含了为对象分配存储空间和初始上述麻烦,使定义类对象时包含了为对象分配存储空间和初始化的双重任务。这种实现机制较为理想。化的双重任务。这种实现机制较为理想。 由于类的唯一性和对象的多样性,因此由于类的唯一性和对象的多样性,因此C+ 规定构造函数规定构造函数与类同名。其特点是:与类同名。其特点是: Constructor is a function with the explicit purpose of initializing object. Because such a function constructs values of a given type, it is called a constructor. A constructor is recognized by having the same name as the class itself. When a class has a constructor, all objects of that class will be initialized by a constructor call.构造函数的使用方式有构造函数的使用方式有:构造函数在类体内定义,构造函数在类体内定义,例如:例如:#include class Desk public: Desk() weight=10; high=5; width=5; length=5; protected: int weight; int high; int width; int length;void fn() Desk da; /constructor call /构造函数可以在类体外定义,例如:构造函数可以在类体外定义,例如:#include class Desk public: Desk(); protected: int weight; int high; int width; int length;Desk:Desk() /constructor definition weight=10; high=5; width=5; length=5; coutweight“ ”high“ ”width“ ”lengthendl;void fn() Desk da;void main() fn();结果:结果:10 5 5 510555da 对象的内存空间分配及初始化对象的内存空间分配及初始化weighthighwidthlengthda若将若将fn() 函数改为函数改为void fn() Desk dd5;则执行定义数组语句时,构造则执行定义数组语句时,构造函数将被调用函数将被调用5 次,即对每个次,即对每个数组成员对象的创建都要调用数组成员对象的创建都要调用一次构造函数。一次构造函数。1055510555weighthighwidthlengthweighthighwidthlengthdd0dd4dd5 对象的内存空间分配及初始化对象的内存空间分配及初始化输出结果为:输出结果为:105 5 510 5 5 5105 5 510 5 5 510 5 5 5注意:注意:1、类体外定义构造函数,其函数名前要加上、类体外定义构造函数,其函数名前要加上“类名类名:”;2、构造函数无返回类型;构造函数无返回类型;3、在定义时,若类的数据成员是另一个类的对象,则在调用、在定义时,若类的数据成员是另一个类的对象,则在调用构造函数创建对象时,对作为数据成员的对象先要自动调用构造函数创建对象时,对作为数据成员的对象先要自动调用其自身的构造函数。其自身的构造函数。下面程序的下面程序的Tutorpair类类(“帮教派对帮教派对”)中,其成员包含有中,其成员包含有学生类对象和老师类对象。程序说明了调用构造函数的方法。学生类对象和老师类对象。程序说明了调用构造函数的方法。/ch11-5.cpp#include class Student public: Student() cout“Constructing student.n”; semeshours=100; gpa=3.5; / protected: int semeshours; float gpa;class Teacher public: Teacher() cout“Constructing teacher.n”; ;class Tutorpair public: Tutorpair() cout“Constructing tutorpair.n”; nomeetings=0; protected: Student student; /class object as member data Teacher teacher; /class object as member data int nomeetings;void main() Tutorpair tp; cout“Back in main.n”;运行结果:运行结果:Constructing student.Constructing teacher.Constructing Tutorpair.Back in main. 当调用构造函数当调用构造函数Tutorpair() 创建的对象创建的对象tp 时,首先时,首先根据对象成员被说明的次序,依次调用根据对象成员被说明的次序,依次调用Student() 构造函构造函数和数和Teacher() 构造函数创建成员对象构造函数创建成员对象Student 和和Teacher,然后执行它自己的构造函数的函数体,如运行然后执行它自己的构造函数的函数体,如运行结果所示。结果所示。 The constructor is used to initialize objects, and destructor is used to clean up objects and release resources before they are become unusable. 例如在下面情况下需要使用析构函数:一个对象当结束其例如在下面情况下需要使用析构函数:一个对象当结束其生命期时,比如在函数体内定义的对象,当该函数调用结束时,生命期时,比如在函数体内定义的对象,当该函数调用结束时,局部对象被释放。局部对象被释放。 构造函数打开一个文件,使用完文件时,需要关闭文件。构造函数打开一个文件,使用完文件时,需要关闭文件。 从堆中分配了动态内存区,在对象消失之前必须释放。从堆中分配了动态内存区,在对象消失之前必须释放。11.3 析构函数析构函数 析构函数的特点:析构函数的特点: 无返回类型;无返回类型; 无参数;无参数; 不能随意调用;不能随意调用; 不能重载。不能重载。 而构造函数可以有参数,也可以重载。而构造函数可以有参数,也可以重载。 析构函数与构造函数的功能相对应,所以析构函数名是构析构函数与构造函数的功能相对应,所以析构函数名是构造函数名前加一个逻辑反运算符造函数名前加一个逻辑反运算符“”例如:例如:下面程序段在下面程序段在class Xyz 类中定义了一个构造函数和一个类中定义了一个构造函数和一个析构函数。析构函数。class Xyz public: Xyz() /constructor definition name=new char20; Xyz() /destructor definition delete name; protected: char *name; 该类该类定义的构造函数在对象之外分配一段堆内存空间,撤定义的构造函数在对象之外分配一段堆内存空间,撤销时,由析构函数收回堆内存。销时,由析构函数收回堆内存。 注意,析构函数以调用构造函数相反的顺序被调用。注意,析构函数以调用构造函数相反的顺序被调用。例如:例如:在在ch11-5.cpp 程序中为每个类增加析构函数则有:程序中为每个类增加析构函数则有:/ch11-7.cpp#include class Student public: Student() cout“Constructing student.n”; semeshours=100; gpa=3.5; Studeng() cout “Destructing student.n”; protected: int semeshours; float gpa;class Teacher public: Teacher() cout“Constructing teacher.n”; Teacher() cout “Destructing teacher.n”; ;class Tutorpair public: Tutorpair() cout“Constructing tutorpair.n”; nomeetings=0; Tutorpair() cout“Destructing tutorpair.n”; protected: Student student; Teacher teacher; int nomeetings;void main() Tutorpair tp; cout“Back in main.n”;Constructing student.Constructing teacher.Constructing tutorpair.Back in main.Destructing tutorpair.Destructing teacher.Destructing student.运行结果:运行结果: 不带不带参数的构造函数不能完全满足初始化的要求,因为这参数的构造函数不能完全满足初始化的要求,因为这样创建的类对象具有相同的初始化值。样创建的类对象具有相同的初始化值。 如果需要对类对象按不同特征初始化不同的值,应采用带如果需要对类对象按不同特征初始化不同的值,应采用带参数的构造函数。如下面程序所示:参数的构造函数。如下面程序所示: /tdate1.h class Tdate1 public: Tdate1(int y,int m,int d); Tdate1(); void print();11.4 带参数的构造函数带参数的构造函数 private: int year,month,day;Tdate1:Tdate1(int y,int m,int d) year=y; month=m; day=d; cout“Constructor called.n”;Tdate1:Tdate1() cout“Destructor called.n”;void Tdate1:print() coutyear“.”month“.”dayendl;#include #include “tdate1.h”void main() Tdate1 today(2001,5,1),tomorrow(2001,5,2); cout“Today is ”; today.print(); cout“Tomorrow is ”; tomorrow.print();结果:结果:Constructor called.Constructor called.Today is 2001.5.1Tomorrow is 2001.5.2Destructor called.Destructor called. 注意注意注意注意:构造函数可采用以下两种方式将值赋给其成员。构造函数可采用以下两种方式将值赋给其成员。1 1、在构造函数体内进行成员变量的赋值,、在构造函数体内进行成员变量的赋值,、在构造函数体内进行成员变量的赋值,、在构造函数体内进行成员变量的赋值,如前例中所示,又如:如前例中所示,又如:class X int a,b; /default private public: X(int i,int j) a=i; b=j; /value assigned within constructor ;2 2、使用函数体前的初始值表使用函数体前的初始值表使用函数体前的初始值表使用函数体前的初始值表,例如:例如: class X int a,b; public: X(int i,int j):a(i),b(j) ; 下面是使用构造函数创建对象,使用析构函数撤销对象的另一下面是使用构造函数创建对象,使用析构函数撤销对象的另一个例子。个例子。/ch11-9.cpp#include #include class Student public: Student(char *pname,int xhours,int xgpa) cout“Constructing student ”pnameendl; strncpy(name,pname,sizeof(name); namesizeof(name)-1=0; semeshours=xhours; gpa=xgpa; Student cout“Destructing”nameendl; /其它成员函数其它成员函数 protected: char name20; int semeshours; float gpa;void main() Student one(“Li ming”,22,3.5); /运行结果:运行结果:Constructing student Li mingDestructing Li ming创建对象创建对象one 的情况如下:的情况如下: 学生名学生名“Li ming” name20 学期学时数学期学时数 22 semeshours 平均成绩平均成绩 3.5 gpa 构造函数可以被重载,构造函数可以被重载,C+ 根据类体中声明构造函数的根据类体中声明构造函数的参数类型和个数选择合适的构造函数。参数类型和个数选择合适的构造函数。例如:例如:下面程序中声明了下面程序中声明了4 个重载的构造函数个重载的构造函数/ch11-9.cpp#include class Tdate public: Tdate(); Tdate(int d); Tdate(int m,int d); Tdate(int m,int d,int y); /.11.5 重载构造函数重载构造函数 protected: int month,day,year;Tdate:Tdate() mothe=4; day=15; year=1995; coutmonth“/”day“/”yearendls;Tdate:Tdate(int d) mothe=4; day=d; year=1996; coutmonth“/”day“/”yearendls;Tdate:Tdate(int m,int d) mothe=m; day=d; year=1997; coutmonth“/”day“/”yearendls;Tdate:Tdate(int m,int d,int y) mothe=m; day=d; year=y; coutmonth“/”day“/”yearendls;void main() Tdate aday; Tdate bday(10); Tdate cday(2,12); Tdate dday(1,2,1998);结果为:结果为: 4/15/1995 4/10/1996 2/12/1997 1/2/1998 A default constructor is a constructor that can be called without supplying an argument. If a user has declared a default constructor. that one will be used; otherwise, the compiler will try to generate one if needed, and if the user hasnt declared other constructors.11.6 缺省构造函数缺省构造函数(Default Constructors)1、C+ 规定,每一个类必须有一个构造函数,没有构造函数就规定,每一个类必须有一个构造函数,没有构造函数就不能创建任何对象;不能创建任何对象;2、若未定义一个类的构造函数,则若未定义一个类的构造函数,则C+ 提供了一个缺省的构造提供了一个缺省的构造函数,该缺省构造函数是一个无参数的构造函数,仅仅负责创函数,该缺省构造函数是一个无参数的构造函数,仅仅负责创建对象,而不做任何初始化工作;建对象,而不做任何初始化工作;3、只要一个类定义了一个构造函数,只要一个类定义了一个构造函数,C+ 就不再提供缺省的构就不再提供缺省的构造函数。如还需要无参数构造函数,则必须自己定义;造函数。如还需要无参数构造函数,则必须自己定义;4、与变量定义类似,在用缺省构造函数创建对象时,如果创建与变量定义类似,在用缺省构造函数创建对象时,如果创建的是全局对象或静态对象,则对象成员数据全为的是全局对象或静态对象,则对象成员数据全为0;局部对象创;局部对象创建时,其成员数据是无意义的随机数。建时,其成员数据是无意义的随机数。 本章第一个例子中,创建的全局对象、静态对象及局部对本章第一个例子中,创建的全局对象、静态对象及局部对象都是由编译器提供的缺省构造函数自动创建的,仅对成员数象都是由编译器提供的缺省构造函数自动创建的,仅对成员数据分配了内存空间,未做初始化工作。据分配了内存空间,未做初始化工作。 C+ 中,提供了用一个对象值创建并初始化另一个对象的中,提供了用一个对象值创建并初始化另一个对象的方法,完成该功能的是拷贝构造函数。例如:方法,完成该功能的是拷贝构造函数。例如: Tdate d1(2002,3,1); Tdate d2(d1); 用构造函数创建用构造函数创建d1 的值初始化新创建的对象的值初始化新创建的对象d2。拷贝构造函数的特点:拷贝构造函数的特点:1、拷贝构造函数名字与类同名,没有返回类型;、拷贝构造函数名字与类同名,没有返回类型;2、拷贝构造函数只有一个形参数,该参数是该类的对象的引用;、拷贝构造函数只有一个形参数,该参数是该类的对象的引用;11.7 拷贝构造函数拷贝构造函数(Copy Constructor)3、拷贝构造函数的格式如下:、拷贝构造函数的格式如下: :(&) 其中,其中, 与该类名相同;与该类名相同;4、如果一个类中没有定义拷贝构造函数,则系统自动生成一个、如果一个类中没有定义拷贝构造函数,则系统自动生成一个缺省拷贝构造函数,其功能是将已知对象的所有数据成员的值缺省拷贝构造函数,其功能是将已知对象的所有数据成员的值拷贝给对应对象的数据成员。拷贝给对应对象的数据成员。例如:下面程序说明了调用拷贝构造函数的过程例如:下面程序说明了调用拷贝构造函数的过程#include class Tpoint public: Tpoint(int xp,int yp) x=xp; y=yp; Tpoint(Tpoint& p); /copy constructor declaration Tpoint() cout“Destructor called.n”; int xcoord() return x; int ycoord() return y; private: int x,y;Tpoint:Tpoint(Tpoint& p) x=p.x; y=p.y; cout“Copy_initialization constructor called.n”;void main() Tpoint p1(5,7); Tpoint p2(p1); cout“p2=”p2.xcoord()“,”p2.ycoord() endl;结果:结果:Copy_initialization constructor called.P2=5,7Destructor called.Destructor called. 拷贝构造函数除了用于使用已知对象的值创建一个同类的拷贝构造函数除了用于使用已知对象的值创建一个同类的新对象外,还有两个主要用处:新对象外,还有两个主要用处:1、把对象作为实参数进行函数调用时,系统自动调用拷贝构造把对象作为实参数进行函数调用时,系统自动调用拷贝构造函数实现把对象值传递给形参对象;函数实现把对象值传递给形参对象;2、当函数的返回值为对象时,系统自动调用拷贝构造函数对返当函数的返回值为对象时,系统自动调用拷贝构造函数对返回对象值创建一个临时对象,然后再将这个临时对象值赋给接回对象值创建一个临时对象,然后再将这个临时对象值赋给接收函数返回值的对象。收函数返回值的对象。例如:例如:下面程序中共创建了下面程序中共创建了7 个对象,使用了个对象,使用了3 次拷贝构造函数。次拷贝构造函数。11.8 拷贝构造函数的其他用处拷贝构造函数的其他用处#include “tpoint.h”Tpoint fun(Tpoint q);void main() Tpoint M(12,20),P(0,0),S(0,0); Tpoint N(M); P=fun(N); S=M;cout“P=”P.xcoord()“,”P.ycoord() endl; cout“S=”S.xcoord()“,”S.ycoord() endl;Tpoint fun(Tpoint q) cout“OKn”; int x=q.xcoord()+10; int y=q.ycoord()+15; Tpoint R(x,y); return R;/tpoint.h#include class Tpoint public: Tpoint(int xp,int yp) x=xp; y=yp; Tpoint(Tpoint& p); Tpoint() cout“Destructor called.n”; int xcoord() return x; int ycoord() return y; private: int x,y;Tpoint:Tpoint(Tpoint& p) x=p.x; y=p.y; cout“Copy_initialization constructor called.n”;输出结果如下:输出结果如下:Copy_initialization constructor called.Copy_initialization constructor called.OKCopy_initialization constructor called.Destructor called.Destructor called.Destructor called. P=22,35P=22,35 S=12,20 S=12,20 Destructor called. Destructor called. Destructor called. Destructor called. Destructor called. Destructor called. Destructor called. Destructor called.程序输出结果说明程序中出现了三次调用构造函数:程序输出结果说明程序中出现了三次调用构造函数:Tpoint N(M); /M 对象创建对象创建N 对象对象P=fun(N); /实参实参N 对象被拷贝到形参对象被拷贝到形参q对象对象return R; /函数返回时,调用拷贝构造函数,用对象函数返回时,调用拷贝构造函数,用对象R创建创建 / 一个临时对象,保存一个临时对象,保存R 的数据,在主函数中临的数据,在主函数中临 / 时对象被释放前,将它的内容赋值到对象时对象被释放前,将它的内容赋值到对象P 中。中。临时对象临时对象P放放 入入取取 出出Rfun()主函数主函数 暂存区暂存区 main()区区临时对象起暂存作用的情况如下图所示:临时对象起暂存作用的情况如下图所示: 构构造造函函数数是是一一种种用用于于创创建建对对象象的的特特殊殊成成员员函函数数,调调用用它它为为类类对对象象分分配配空空间间,给给它它的的数数据据成成员员赋赋初初值值,以以及及其其他他请求资源的工作。请求资源的工作。 析析构构函函数数是是一一种种用用于于撤撤销销对对象象,回回收收对对象象占占有有资资源源的的特殊成员函数,它与构造函数功能互补,成对出现。特殊成员函数,它与构造函数功能互补,成对出现。 每每个个类类对对象象都都必必须须在在构构造造函函数数中中诞诞生生,一一个个类类可可能能定定义义一一个个或或多多个个构构造造函函数数,编编译译程程序序按按对对象象构构造造函函数数声声明明中中使使用用的的形形参参数数与与创创建建对对象象的的实实参参数数比比较较,确确定定使使用用哪哪个个构构造函数,这与普通重载函数的使用方法类似。造函数,这与普通重载函数的使用方法类似。十十 一一 章章 小小 结结 在包含有对象成员的类对象被创建时,需要对对象成在包含有对象成员的类对象被创建时,需要对对象成员进行创建,相应地要调用对象成员的构造函数。员进行创建,相应地要调用对象成员的构造函数。 拷贝构造函数用于由一个已知的对象创建一个新对象。拷贝构造函数用于由一个已知的对象创建一个新对象。 缺省构造函数和缺省析构函数用于在类中未显式定义缺省构造函数和缺省析构函数用于在类中未显式定义构造函数和析构函数的情况,以创建一个对象构造函数和析构函数的情况,以创建一个对象(只分配数据只分配数据成员的存储空间,不能初始化值成员的存储空间,不能初始化值),自动调用缺省的析构函,自动调用缺省的析构函数,以撤销一个对象并回收资源。数,以撤销一个对象并回收资源。
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号