第八章对象生灭_第1页
第八章对象生灭_第2页
第八章对象生灭_第3页
第八章对象生灭_第4页
第八章对象生灭_第5页
已阅读5页,还剩45页未读 继续免费阅读

下载本文档

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

文档简介

1、面向对象程序设计(二)吕俊白第八章对象生灭第八章对象生灭n主要内容:主要内容: 定义构造函数;定义构造函数; 构造函数的运行机制;构造函数的运行机制; 定义析构函数;定义析构函数; 析构函数的运行机制;析构函数的运行机制;n重点:重点:构造函数、析构函数。构造函数、析构函数。概述概述nC+的构造函数和析构函数是类的的构造函数和析构函数是类的特殊特殊成员成员函数,是为了保证类的封装特性而设置的。在函数,是为了保证类的封装特性而设置的。在程序设计中通过定义构造函数和析构函数能够程序设计中通过定义构造函数和析构函数能够轻松灵活地创建和撤消类对象。轻松灵活地创建和撤消类对象。当对象被建立时,构造函数马

2、上被调用,自动当对象被建立时,构造函数马上被调用,自动地给对象分配空间并进行初始化;地给对象分配空间并进行初始化;当对象生命期结束(撤消)时,析构函数由系当对象生命期结束(撤消)时,析构函数由系统自动调用,释放对象所占有的资源。统自动调用,释放对象所占有的资源。构造函数和析构函数完善了构造函数和析构函数完善了C+的类机制。的类机制。8.1 构造函数构造函数1为什么要引入构造函数为什么要引入构造函数n面向对象方法中的类是一组具有相同属性和行面向对象方法中的类是一组具有相同属性和行为的对象的抽象。为的对象的抽象。类描述的是一组事物所具有的共同性质。类描述的是一组事物所具有的共同性质。对象是类的实例

3、,是确实存在的实体,它具有对象是类的实例,是确实存在的实体,它具有确定的属性。确定的属性。由于对象的意义表达了现实世界的实体,它具有由于对象的意义表达了现实世界的实体,它具有确定的属性,因此,一旦建立对象,就必须要确定的属性,因此,一旦建立对象,就必须要有一个有意义的初始值。有一个有意义的初始值。2. 定义构造函数定义构造函数由于对象的一部分属性(即类的一部分数据成员)由于对象的一部分属性(即类的一部分数据成员)是私有的是私有的(private)(private)、被保护的、被保护的(protected)(protected),只允许类的成员函数来访问它,所以,对象的只允许类的成员函数来访问它

4、,所以,对象的初始化任务,必须由类的成员函数来完成。构初始化任务,必须由类的成员函数来完成。构造函数就是这样一个特殊的成员函数。造函数就是这样一个特殊的成员函数。 (1)构造函数的命名构造函数的命名构造函数名必须与类名相同。构造函数名必须与类名相同。 (2)定义构造函数定义构造函数我们可以象定义其它成员函数一样定义构造函数。我们可以象定义其它成员函数一样定义构造函数。即:即:构造函数可以在类的内部定义,也可以在构造函数可以在类的内部定义,也可以在类的外部定义。类的外部定义。例如:例如:(P:297)定义一个平面坐标类定义一个平面坐标类Ppoint,使用该类以计算坐标点的直,使用该类以计算坐标点

5、的直角坐标所对应的极坐标。(角坐标所对应的极坐标。(使用了头文件卫士使用了头文件卫士P:246)/ ppoint.h#ifndef HEADER_PPOINT#define HEADER_PPOINTclass PPoint double x, y; /直角坐标直角坐标public: PPoint(double ix, double iy); / 构造函数声明构造函数声明:设置坐标设置坐标 double xOffset(); /直角坐标直角坐标x分量分量 double yOffset(); /直角坐标直角坐标y分量分量 double angle(); /极坐标弧角极坐标弧角 double ra

6、dius(); /极坐标半径极坐标半径;/=#endif / HEADER_PPOINT / ppoint.cpp#includeppoint.h#includePPoint:PPoint(double ix, double iy) /构造函数定义构造函数定义 x=ix; y=iy; double PPoint:xOffset() return x;double PPoint:yOffset() return y;double PPoint:angle() return (180/3.14159)*atan2(y,x);double PPoint:radius() return sqrt(x*

7、x+y*y); / f0902.cpp using PPoint#includeppoint.h#includeusing namespace std;int main() for(double x,y; coutxy & x=0; ) PPoint p(x,y); coutangle=p.angle(), radius=p.radius(), x offset= p.xOffset(), y offset=p.yOffset()n; return 1;/= 注意:注意:1)C+规定:构造函数名必须与类名相同,规定:构造函数名必须与类名相同,它在该类对象创建时自动被调用。它在该类对象创

8、建时自动被调用。2)构造函数无返回类型。构造函数无返回类型。构造函数没有返回类型,函数体中也不允构造函数没有返回类型,函数体中也不允许包含返回值语句,但可以有无值返回许包含返回值语句,但可以有无值返回语句语句“return;”。例如:例如:class Person public: int Person() /return 1;/;错误:错误:1、构造函数是无返回类型的;、构造函数是无返回类型的; 2、构造函数体内只能使用无值返回语句、构造函数体内只能使用无值返回语句class Personpublic:Person()/正确正确 / return;/正确正确/;注意:注意:3)在类外部定义的构

9、造函数,其函数名前要加上在类外部定义的构造函数,其函数名前要加上“类名类名:”。4)直接以类名(构造函数名)调用构造函数,则直接以类名(构造函数名)调用构造函数,则产生一个无名对象。产生一个无名对象。例如:例如:PPoint(3,5);无名对象一般用在创建后不需要反复使用的场合,无名对象一般用在创建后不需要反复使用的场合,常在参数传递时用到。常在参数传递时用到。8.2构造函数的重载构造函数的重载/ f0903.cpp 构造函数重载构造函数重载#include#includeusing namespace std;class Date int year, month, day;public: D

10、ate(int y=2000, int m=1, int d=1); / 设置默认参数设置默认参数 Date(const string& s); / 重载重载 bool isLeapYear()const; void print();8.2.1重载构造函数重载构造函数n构造函数不但可以重载,还可以设置默认参数。构造函数不但可以重载,还可以设置默认参数。例如:例如: (P:299)Date:Date(const string& s) year = atoi(s.substr(0,4).c_str();/如:如: 2008-05-12 month = atoi(s.substr(5

11、,2).c_str(); day = atoi(s.substr(8,2).c_str();/-Date:Date(int y, int m, int d) year=y,month=m,day=d; bool Date:isLeapYear()const return (year % 4=0 & year % 100 )| year % 400=0;void Date:print() coutsetfill(0)setw(4)year-setw(2) month-setw(2)daynsetfill( );int main() Date c(2005-12-28); Date d(2

12、003,12,6); Date e(2002); / 默认两个参数默认两个参数 Date f(2002,12); / 默认一个参数默认一个参数 Date g; / 默认三个参数默认三个参数 c.print ();d.print ();e.print ();f.print ();g.print (); return 1;/=当处理过程不同时,使用当处理过程不同时,使用重载;当处理过程类似时,重载;当处理过程类似时,用默认参数可以省去一些用默认参数可以省去一些重复的编码工作。重复的编码工作。8.2.2无参数构造函数无参数构造函数n无参构造函数也称默认构造函数,系统默认,类类型无参构造函数也称默认构

13、造函数,系统默认,类类型定义中若无构造函数,照样可以用对象定义语句创建定义中若无构造函数,照样可以用对象定义语句创建对象。对象。即:类机制中总是为无构造函数的类默认地建立一个无即:类机制中总是为无构造函数的类默认地建立一个无参的构造函数,它除了分配对象的实体空间外,其他参的构造函数,它除了分配对象的实体空间外,其他什么也不做。什么也不做。例如:例如:class Date public: /类定义中无构造函数类定义中无构造函数 / 相当于定义了相当于定义了Date();int main() Date d; / ok / .n注意:注意:如果手工定义了无参构造函数,或者任何其如果手工定义了无参构造

14、函数,或者任何其他的构造函数,则系统不再提供默认的无参构造函数。他的构造函数,则系统不再提供默认的无参构造函数。例例1:class Date public: Date(int y, int m, int d) / .;int main() Date d; / error / .例例2:class A/私有成员私有成员public:A(int x)A(string s)/其他公有成员其他公有成员;/-A a(2);A c;/错:错:no appropriate default constructor available8.3类成员初始化类成员初始化8.3.1默认调用的无参数构造函数默认调用的无参

15、数构造函数n类定义中有数据成员和成员函数,数据类定义中有数据成员和成员函数,数据成员可以是内部数据类型的变量实体,成员可以是内部数据类型的变量实体,也可以是对象实体。也可以是对象实体。例如:有一个学号类和一个学生类,学生类中包例如:有一个学号类和一个学生类,学生类中包含了学号类的对象,这样在构造学生类对象时,含了学号类的对象,这样在构造学生类对象时,就面临着学号类对象的构造。就面临着学号类对象的构造。/ f0905.cpp 对象成员的默认构造对象成员的默认构造#include#include using namespace std;class StudentID/学号类学号类 int valu

16、e;public: StudentID( )/无参数构造函数无参数构造函数 static int nextStudentID = 0; /静态局部变量静态局部变量 value = +nextStudentID; coutAssigning student id valuen; ;/-class Student/学生类学生类 string name; StudentID id;/对象成员对象成员public: Student(string n = noName) cout Constructing student + n + n; name = n; ;/-int main() Student

17、s(Randy); return 1;/=nC+的类机制对于含有对象成员的类对象的的类机制对于含有对象成员的类对象的构造制定了一些规则,其内部的执行次序如下:构造制定了一些规则,其内部的执行次序如下:(1)分配对象空间;分配对象空间;(2)根据在类中声明的根据在类中声明的对象成员对象成员的次序依次调用的次序依次调用其构造函数;其构造函数;(3)执行自己的构造函数。执行自己的构造函数。8.3.2 成员的初始化成员的初始化n若要调用对象成员的有参构造函数,须显式调用。其方若要调用对象成员的有参构造函数,须显式调用。其方法为:法为:在构造函数的参数列表右括号后面,花括号前面,在构造函数的参数列表右括

18、号后面,花括号前面,用冒号引出构造函数的调用表。用冒号引出构造函数的调用表。例如:例如:/ f0907.cpp initialization of class members#include#include using namespace std;class StudentID int value;public: StudentID(int id=0) value=id; cout Assigning student id value endl; ;/-class Student string name; StudentID id;public: Student(string n=no nam

19、e, int ssID=0):id(ssID),name(n) coutConstructing student nn; ;/-int main() Student s(Randy, 98); Student t(Jenny); return 1;相当于:相当于:StudentID id(ssID);n对于常量成员和引用成员的初始化也可用构造对于常量成员和引用成员的初始化也可用构造参数表的方式解决。例如:参数表的方式解决。例如:P306.cpp#include using namespace std;class Silly const int ten;int &ra;public:Si

20、lly(int x,int &a):ten(x),ra(a)void print()coutten=ten,ra=raendl;int main() int a=20; Silly sy(10,a); sy.print(); return 1;小结:小结:n对于变量成员有两种初始化方式:对于变量成员有两种初始化方式:例如:例如:class Date int year, month, day;public: Date(int y=2000, int m=1, int d=1); / 设置默认参数设置默认参数 /;1.Date:Date(int y,int m,int d)year=y,m

21、onth=m,day=d;2.Date:Date(int y,int m,int d):year(y),month(m),day(d)8.4构造顺序构造顺序nC+给构造对象的顺序做了专门的规定:给构造对象的顺序做了专门的规定:局部对象局部对象v局部对象和静态局部对象构造的顺序与它们在程局部对象和静态局部对象构造的顺序与它们在程序中出现的顺序是一致的。序中出现的顺序是一致的。C+是根据运行中是根据运行中定义对象的顺序来决定对象的创建顺序,而且,定义对象的顺序来决定对象的创建顺序,而且,静态局部对象只创建一次。静态局部对象只创建一次。例如:例如:/ f0908.cpp/ test local ob

22、ject create order#includeusing namespace std;class Apublic: A() cout; ;/-class Bpublic: B() cout; ;/-class Cpublic: C() cout; ;/-void func() coutnfunc: ; A a; static B b; /静态局部对象静态局部对象 C c;/-int main() coutmain: ; for(int i=1; i=2; +i) for(int j=1; j=2; +j) if(i=2) C c; else A a; B b; func(); func()

23、; return 1;全局对象全局对象n和全局变量一样,所有全局对象在主函数和全局变量一样,所有全局对象在主函数(main)启动之前全部已被构造。启动之前全部已被构造。n全局对象的调试方法:全局对象的调试方法:(1)将全局对象先作为局部对象来调试;将全局对象先作为局部对象来调试;(2)在所有怀疑有错的构造函数的开头增加输出在所有怀疑有错的构造函数的开头增加输出语句语句n全局对象没有明确的控制流来表明其构造顺序。全局对象没有明确的控制流来表明其构造顺序。n全局对象的创建顺序在标准全局对象的创建顺序在标准C+中没有规定,中没有规定,一切视编译器的内在特性而定。一切视编译器的内在特性而定。例如:例如

24、:/ student.h#includeusing namespace std;class Student const int id;public: Student(int d):id(d) coutstudentn; void print() coutidn; ;/-class Tutor Student s;public: Tutor(Student& st):s(st) couttutorn; s.print(); ;/-/ f0909.cpp test global object create order#includestudent.hextern Student ra;/-

25、Tutor je(ra); /全局对象全局对象int main()return 1; / f09092.cpp test global object create order#includestudent.hStudent ra(18); /全局对象全局对象注意:注意:为了避免编译器实现中的不确定问题,应尽为了避免编译器实现中的不确定问题,应尽量不要设置全局对象,更不要让全局对象之间互相量不要设置全局对象,更不要让全局对象之间互相依赖。依赖。成员对象成员对象n成员对象以其在类中声明的顺序构造。成员对象以其在类中声明的顺序构造。例如:例如:/ f0910.cpp test member obje

26、ct create order#includeusing namespace std;class Apublic: A(int x) coutA:x; ;/-class Bpublic: B(int x) coutB:x; ;/-class C A a; B b;public: C(int x,int y):b(x),a(y) coutCn; ;/-int main() C c(15, 9); return 1;构造位置构造位置n全局对象、常对象、静态对象都放在全全局对象、常对象、静态对象都放在全局数据区;局数据区;n在函数中定义的局部对象,放在栈区;在函数中定义的局部对象,放在栈区;n用用n

27、ew申请的空间都在动态内存区(即申请的空间都在动态内存区(即堆区)。堆区)。8.5拷贝构造函数拷贝构造函数 (P:315)n拷贝构造函数是一种拷贝构造函数是一种特殊特殊的构造函数,具有一的构造函数,具有一般构造函数的所有特性。般构造函数的所有特性。n拷贝构造函数的拷贝构造函数的形参是本类对象的引用形参是本类对象的引用。n拷贝构造函数的作用是:使用一个已经存在的拷贝构造函数的作用是:使用一个已经存在的对象(由拷贝构造函数的参数指定的对象),对象(由拷贝构造函数的参数指定的对象),去初始化一个新的去初始化一个新的同类对象同类对象。用户可以根据自己实际问题的需要定义特定的拷用户可以根据自己实际问题的

28、需要定义特定的拷贝构造函数,以实现同类对象之间数据成员的贝构造函数,以实现同类对象之间数据成员的传递。传递。定义拷贝构造函数的一般形式为:定义拷贝构造函数的一般形式为:class 类名类名 public: 类名(形参表);类名(形参表);/构造函数构造函数 类名(类名类名(类名 对象名);对象名); /拷贝构造函数拷贝构造函数 ;类名类名:类名(类名类名(类名 对象名)对象名)/拷贝构造函数的实现拷贝构造函数的实现函数体函数体 另一常见的形式另一常见的形式: class 类名类名 public: 类名(形参表);类名(形参表);/构造函数构造函数 类名(类名(const 类名类名 对象名);对

29、象名); /拷贝构造函数拷贝构造函数 ; 类名类名:类名(类名(const类名类名 对象名)对象名)/拷贝构造函数的实现拷贝构造函数的实现函数体函数体 用用const限定引用的目的是限定引用的目的是:为了确保参数对象的所有数据成为了确保参数对象的所有数据成员值在拷贝构造函数体内不会被改变。员值在拷贝构造函数体内不会被改变。#include using namespace std;class Point public:Point (int xx=0,int yy =0);Point(const Point &p);/声明拷贝构造函数声明拷贝构造函数int GetX( ) return X

30、;int GetY( ) return Y;private: int X,Y;Point:Point (int xx,int yy)X=xx;Y=yy;cout构造函数被调用构造函数被调用endl;Point:Point(const Point &p)/拷贝构造函数的实现拷贝构造函数的实现X=p.X;Y=p.Y;cout拷贝构造函数被调用拷贝构造函数被调用endl;例如:例如:Copy_Constructor.cpp注意:注意:普通构造函数是在对象创建时被调用,而普通构造函数是在对象创建时被调用,而拷贝构拷贝构造函数在以下三种情况下都会被调用:造函数在以下三种情况下都会被调用:(1)当

31、用类的一个对象去初始化该类的另一个对当用类的一个对象去初始化该类的另一个对象时。象时。int main( )Point A(1,2);Point B(A);cout对象对象B的的X坐标:坐标:B.GetX()endl;return 1;(2)如果如果函数的形参是类的对象函数的形参是类的对象,调用函数,进,调用函数,进行形参和实参结合时。行形参和实参结合时。void f(Point p)coutp.GetX()endl;int main()Point A(1,2);f(A);return 1;(3)如果如果函数的返回值是类的对象函数的返回值是类的对象,函数调用完,函数调用完成返回时。成返回时。P

32、oint g() Point A(1,2);return A;int main()Point B;coutCalling g.endl;B=g();return 1;n如果程序员没有为类声明拷贝构造函数,则编如果程序员没有为类声明拷贝构造函数,则编译器自己生成一个拷贝构造函数。译器自己生成一个拷贝构造函数。n该构造函数的功能是:该构造函数的功能是:把参数对象的每个数据把参数对象的每个数据成员的值都复制到新建立的对象中。成员的值都复制到新建立的对象中。例如:例如:/ f0912_modi.cpp constructing object from other object#includeusing

33、 namespace std;class Person char* pName;/P:315图图9-3public: Person(char* pN=noName) coutConstructing pNn; pName = new charstrlen(pN)+1; if(pName) strcpy(pName,pN); void print() coutName:pNameendl;/-int main() Person p1(Randy); Person p2(p1); p2.print(); return 1;修改修改/ f0912_modi.cpp constructing obje

34、ct from other object(新增析构函数)(新增析构函数)#includeusing namespace std;class Person char* pName;public: Person(char* pN=noName) coutConstructing pNn; pName = new charstrlen(pN)+1; if(pName) strcpy(pName,pN); void print() coutName:pNameendl;Person()/析构函数析构函数 cout Destructing pNamen; delete pName; ;/-int mai

35、n() Person p1(Randy); Person p2(p1); p2.print(); return 1;原因:析构原因:析构p2时将存有时将存有Randy的空间先行释放了,轮到析的空间先行释放了,轮到析构构p1时,时,Randy已经不复存在了。已经不复存在了。/ f0913.cpp copy constructor#includeusing namespace std;class Person char* pName;public: Person(char* pN=noName) coutConstructing pNn; pName = new charstrlen(pN)+1; if(pName) strcpy(pName,pN); Person(const Person& s)/拷贝构造函数拷贝构造函数 coutcopy Construct

温馨提示

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

评论

0/150

提交评论