版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第九章 对象生灭,十分重要、有一点难度的章节,内容概要,构造函数的概念,1,类成员的初始化,2,构造顺序,3,析构函数,4,完善的类机制,C+的出现是为了让程序更安全,可靠,易用 目前为止,我们学习的类的知识还处于一个很脆弱、初级的阶段,初始化是一个大问题,对局部变量的初始值编译器没有强行要求,如果使用一个未赋值的量,编译器或许会给出警告,class String char* s; int len; public: void copy(const char *s1) strcpy(s,s1); void print() coutsendl; ; int main() String s; s.c
2、opy(abcd); s.print(); ,构造函数,引入对象后,因为对象不再是“单一”的变量,初始化的工作会更多 如对“对象”进行有效性的判定 安全的程序要求把人为不可靠因素降到最低 自动完成初始化 类封装性要求对象的私有成员不能从外部初始化 C+中引入构造(constructor)函数完成以上工作,构造函数的定义,构造函数是特殊的类成员函数 在创建对象时自动调用,不能主动调用 返回值:没有 构造函数负责创建对象的实体,如果构造函数出错,程序该终止 函数名:类名 参数:根据需要,构造函数实例,构造函数和其他成员函数一样,可以把定义体写在类外部 有了构造函数,不再是凭空创建对象了. 一个具体
3、的对象,诞生的时候就可以有初始值,定义对象时, 根据构造函数的要求带上参数 如果不带参数,Point a 则不满足创建对象的规则,编译器将报错,class Point int xArray,yArray; public: Point(int x, int y) xArray = x; yArray = y; void set(int x, int y) xArray = x; yArray = y; void print( ) coutxArrayyArray; ; main() Point a(1,2); a.print( ); ,一次性对象,创建对象不一定给出对象名:即直接以类名调用构造函
4、数,则产生一个无名对象,如 coutPoint(2,3); 一次性对象通常在参数传递时用到 不允许以对象名形式调用“构造函数” p1.Point(2,3); 错误 构造函数是对象创建的第一步,构造函数也只是在“创建”对象时才被使用,构造函数重载,一个对象的创建往往是通过多种方式的, 如我们在输入一个日期时,不同地方的人有不同的输入习惯: 1998/1/20, 1/20/1998 (月日年) ,或用字符串形式 创建对象是否可以形式多样化,不那么呆板呢? 构造函数允许重载 允许存在多个构造函数,根据参数类型及个数自动选择,构造函数重载实例,class Date int year,month,day
5、; public: Date(const string ,int main() Date d(“2000-1-20”); /1个字符串参数 Date e(2000, 12, 25); /3个参数 ,Date: Date(const string string在iostream中定义,一个string对象不是简单的ASCII串,类中包含有更多的信息。 string类有丰富的成员函数,使用很方便 见cpp-ref文档 学会使用类是一个基本能力,一个错误的例子,/定义构造函数, 可以如下构造日期类 int main() /月日年格式 Date d(12, 25, 2001); / 年月日格式 Dat
6、e e(2000, 12, 25); ,class Date int year,month,day; public: Date(int y, int m , int d); Date(int m, int d , int y); ; Date:Date(int y,int m, int d) year=y; month=m; day=d; Date:Date(int m, int d , int y) month=m; day=d; year=y; ,构造函数的默认参数,/如何让下面的应用可行呢? int main() Date d( ); / Date e(2000, 12, 25); /
7、,class Date int year,month,day; public: Date(int , int, int ); ; Date:Date(int y,int m, int d) year=y; month=m; day=d; ,Date( );,Date: Date( ) year = ? month=? day = ? ,Date: Date( ) year = 1900; month= 1; day = 1; ,怎样让类更简洁一点?,程序的Date d() 语句有问题,class Date int year,month,day; public: Date(int y=1900,
8、 int m=1, int d=1); void pr() coutyear; ; Date:Date(int y,int m, int d) year=y; month=m; day=d; int main() Date d( ); Date e(2000, 12, 25); d.pr( ); ,是定义一个日期类对象么?,对比: int max( int a, int b); int myFun( );,可以看作是一个函数名为d,没有参数,返回值类型是Date类型的函数,无参构造函数,为避免歧义, C+规定: Date d( ); 声明函数d, 返回值是Date Date d; 定义对象,
9、使用无参构造函数创建对象 即使用无参构造函数不能带() 无参构造函数也称为“默认构造函数” 参数带有默认值也可以称为“默认构造函数”,即在使用构造函数时可以不带参数,构造函数的疑惑,类可以“没有”构造函数,但实际上对象的创建是构造函数负责的 当类没有写构造函数时,编译器会自动生成一个无参构造函数,此时,局部对象的数据成员将初始化为随机数据,class Date int year , month, day; public: void set(int y,int m,int d) year=y;month=m;day=d; void pr( ) coutyearmonthdayendl; ; in
10、t main() Date d; d.pr( ); ,class Date int year; public: void set(int y) year=y; ; int main() Date d; ,class Date int year; public: Date( ) void set(int y) year=y; ; int main() Date d; /可行 空构造函数:不做实际的工作,即year是随机值,=,无参构造函数的“自动生成”,如果已经定义了一个构造函数(不论是有参还是无参), 系统不会再帮我们生成一个默认构造函数 在类的设计时要根据情况决定是否提供默认构造函数,cla
11、ss Date int year , month, day; public: Date(int y, int m, int d) year=y; month=m; day=d; ; int main() Date e(2000, 12, 25); Date h; / 错误 ,9.3 类成员初始化,构造函数负责创建对象,但实际上类成员的初始化工作不是在构造函数内部完成的,class Student float,普通变量和对象初始化的形式: int a = 1; int b(3); const int nn =5; int 类定义中不允许成员写成上面的形式,但初始化在很多时候是必须的,构造顺序对象
12、成员,程序输出为: A: B: 说明对象的构造顺序是:先构造内部对象 先执行的A的构造函数,然后才是B的 构造任何对象都是需要调用“构造函数”来进行的,但构造函数可能会需要参数,class A public: A()cout A:endl; ; class B A a; public: B()cout B:endl; ; int main() B b; ,初始化问题,分析程序,看看问题出现在哪里?(构造顺序问题),class Date int y,m,d; public: Date(int yy,int mm,int dd) y = yy,m=mm, d =dd; void set(int y
13、y,int mm,int dd) y = yy,m=mm, d =dd; ; class Student string name; Date d; public: Student(int a,int b,int c,string s) d.set(a,b,c); name = s; ; main() Student s(1981,1,1,”sandy”); ,虽然Student的构造函数中使用d.set对对象d进行赋值,但注意这个操作应该在Date的构造函数之后进行 问题关键:Date构造函数需要参数,目前程序没有将参数传递给Date,即d没有初始化 Date d; 是不合法的 类需要提供一种
14、初始化机制,构造函数的初始化过程,构造函数内部不是“初始化”过程,而是“执行”过程 对比 int a=3; int a; a=3;,class Student string name; Date d; public: Student(int a,int b,int c,string s) d.set(a,b,c); name = s; ;,Student的构造函数里面的: name = s; d.set(a,b,c)都不是初始化语句,实际这个类没有初始化语句 显然,就Date对象d而言,不初始化是不行的,构造参数表,类成员的初始化需要使用构造参数表,class Date int year ,
15、month, day; public: Date(int y, int m, int d) : year(y),month(m),day(d) ;,以冒号开始, 成员变量后跟一对括号和参数形式, 多个成员变量之间使用, 分隔 表示以括号内的实参去初始化前面写的成员变量. 相当于 : int year=y 的形式 当类成员是“对象”时,会调用对象的“构造函数”进行初始化,int year = y, month =m, day =d;,对象成员,类的成员可以是其他类对象 对象和变量的区别 变量可以不初始化 有些对象必须“初始化” 类的构造函数决定了对象的创建规则,有些类要求对象创建必须带参数,有些
16、则不需要,class A int x; ; class B A a; int y; ; main() A a; B b; ,class A int x; public: A(int t =1) x = t; ; class B A a; int y; ; main() A a; B b; ,class A int x; public: A(int t) x = t; ; class B A a; int y; ; main() A a; B b; ,A a; 应改为 A a(1); 形式 B b;呢?,改正,class A int x; public: A(int t) x = t; ; cl
17、ass B A a; int y; ; main() A a; B b; ,class A int x; public: A(int t) x = t; ; class B A a; int y; public: B(int m):a(m) ; main() A a(1); B b(2); ,class A int x; public: A(int t) x = t; ; class B A a; int y; public: B(int m,int n):a(m) y = n; ; main() A a(1); B b(2,3); ,使用构造参数表修改程序,再看看构造函数体是否需要,clas
18、s Date int y,m,d; public: Date(int yy,int mm,int dd) y = yy,m=mm, d =dd; void set(int yy,int mm,int dd) y = yy,m=mm, d =dd; ;,class Student string name; Date d; public: Student(int a,int b,int c,string s) d.set(a,b,c); name = s; ; main() Student s(1981,1,1,”sandy”); ,Student(int a,int b,int c,string
19、 s):d(a,b,c), name(s) d. set(a,b,c); name = s; ;,使用构造参数表修改程序,Date类的set函数是否必要呢?,class Student string name; Date d; public: Student(int a,int b,int c,string s):d(a,b,c), name(s) /构造函数体可以为空,这里不需要其他内容 ; main() Student s(1981,1,1,”sandy”); ,class Date int y,m,d; public: Date(int yy,int mm,int dd) y = yy,
20、m=mm, d =dd; void set(int yy,int mm,int dd) y = yy,m=mm, d =dd; ;,构造函数初始化列表的使用,初始化列表在以下情况下是必须的: 当有成员对象,并且成员对象没有默认构造函数 常量成员 引用成员,class Student float,构造顺序,类中的对象成员先构造 根据类定义的对象顺序决定对象的创建顺序: 定义一个对象,就调用构造函数.静态对象只创建一次,分析程序的结果,class A public: A( ) cout”; ; /类A class B public: B( ) cout”; ; /类B class C A a; p
21、ublic: C( ) cout”; ; /类C void func() coutn func: ; A a; static B b; C c; int main() func( ); func( ); ,func: A-B-A-C- func: A-A-C-,全局对象,全局对象在main函数启动之前生成,而调试则在main函数启动之后故全局对象不利于调试,#include class worldpublic: world( ) std:cout Hello World!n;world MyWorld;int main( ) ,构造位置,全局数据区: 全局对象,静态全局对象,静态局部对象,常对
22、象 类的静态数据成员也存放在该数据区 栈区: 局部对象(特性:先进后出) (根据不同编译器的实现方法,临时对象可能在栈区,也可能在动态存储区,也可能一部分在栈区,一部分在动态存储区) 动态存储区(也称堆区): 用new、malloc申请的对象,new 运算符,new比malloc好的地方: new可以自动计算所要分配的内存的类型的大小 new能够自动返回正确的指针类型,不必对返回指针进行类型转换 new将分配的对象初始化 (自动调用构造函数) new还可以被重载,使用new操作符,new返回指针 new 类型分配对象个数 (参数或者初始值) int *p = newint; int *p =
23、newint(23); int *p = newint4; int *p = newint4(5); vector *pv = newvector100; Point *p = newPoint(3,2); /调用构造函数,归还内存:delete p; delete pv;,拷贝构造函数的引入,分析下面的语句是否等同:,class Point int xArray; int yArray; public: Point( int x=0, int y=0) xArray = x; yArray = y; void pr( ) coutxArrayyArray; ; int main() Poin
24、t a(3, 4); Point b; b=a; b.pr( ); ,Point b=a;,Point b(a);,Point b; b=a;,对象是由构造函数创建 构造函数根据参数创建,根据初始化形式,说明Point类应该有一个构造函数,这个构造函数有1个参数,参数类型是Point Point ( Point x) ,拷贝构造函数,若类中没有定义拷贝构造函数,则系统会自动定义一个默认拷贝构造函数: Point (const Point int yArray; int* p; public: Point( int x=0, int y=0, int z=0) xArray = x;yArray
25、 = y; p = new int(z); void setP(int cp) *p = cp; void pr( ) coutxArrayyArray*pendl; ; int main() Point a(3, 4, 5); Point b(a); a.setP(6); a.pr( ); b.setP(7); b.pr(); ,”值拷贝让ab两个对象的p指向了一个地方,有指针成员时,对象的“复制”不能是浅层的,对象a,3,4,0 x100,堆内存地址 0 x100,5,对象b,3,4,0 x100,6,自定义拷贝构造函数,空拷贝构造函数负责将传递的对象到新创的对象做对象本体的位对位拷贝(甚
26、至连指针值都相等,即与参数对象拥有共同的资源) 一般当类有指针成员时需要自定义拷贝构造函数 为了防止这种情况出现, 我们需要自己定义拷贝构造函数, 对象之间的复制或对象由另一对象创建时, 需为指针重新分配内存 拷贝构造函数头 Point( const Point Point b(a); a.setP(5); ,class Point int xArray; int yArray; int* p; public: Point( int x=0, int y=0, int z=0) xArray = x;yArray = y; p = new int(z); Point( const Point,
27、析构函数 ( Destructors ),对象结束其生命时,会被系统销毁(析构).即对象本体空间与名字脱离关系. 对象结束生命时,若对象有指针,则需要人为地进行资源释放,以保证对象本体失效之前,资源被收回.(成员指针所指向的内存区域) 关于内存泄露(memory leak) 对象或变量需要使用内存 局部变量(栈内存)在函数结束后会“自动回收” 动态申请(堆内存)系统不会自动释放 程序结束后系统回收所有资源 内存泄露一般指运行时 内存泄露的一个实例,new动态内存分配,程序结束才释放内存 若改为 int p10000; 则不存在;,#include #include using namespac
28、e std; void fun() for(int j=0;j1000;j+) int* p = new int10000; int main() for(int i=0;i10;i+) fun(); cout第i次调用endl; getch(); return 0; ,析构函数的定义,当对象消失时要做一些“善后”工作,这些工作在析构函数内完成 对象消失自动调用析构函数 析构函数 函数名 类名 没有参数,没有返回值(故析构函数不能重载) 对象析构的顺序和对象创建的顺序相反 入栈和出栈的顺序,构造和析构的顺序,main: A-ok-B-C-ok-C-C-A-B,class A public: A
29、( ) cout; A( ) cout; B( ) cout; C() cout”; 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(); ,什么时候需要析构函数,当构造函数中有动态内存申请,或者有指针成员时,应该写析构函数,在内释放内存,class T int data; int* p; public: T(int i, int j) data = i; p=new int( j );
30、 T( ) delete p; ; int main(void)T t; ,小结:类背后的故事,我们定义一个类时, 系统/编译器可能为我们生成了些什么?,class T int a; ;,class T int a; /无参构造,析构函数 T( ) T( ) /拷贝构造函数 T( const T public: Student(const string ,fn的参数类型是Student 实参的类型是string 参数可以传递的原因: Student的构造函数允许string转换 为Student,关于类型转换,C语言可以隐形转换(自动),如 int a= 4.5 ; float f = 5.6/2; 可以强制类型转换,常用
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 中医学基础理论病机
- 医师节-百年薪火・光影三院摄影比赛通知
- CAS40合营安排赵国强
- GBF蜂巢芯空心楼盖施工过程
- enu与疾病动物模型
- 公司经营管理部员工试用期转正个人总结
- 2026北师大二下有余数除法教学课件
- Bondvaluation债券的价值分析
- 安全隐患专项报告
- 2026年计算机数据库设计师模拟卷
- 2026湖南衡阳市衡东县第二批事业单位公开选调工作人员88人笔试备考题库及答案详解
- JBT 8521.2-2025 编织吊索 安全性 第2部分:一般用途合成纤维圆形吊装带标准立项发展报告
- 2026拖拉机驾驶证科目一理论考试复习题库(含完整答案)
- 贯彻落实《全国党员教育培训工作规划(2024-2028年)》中期评估的工作报告
- 快递柜加盟合同范本
- 医疗AI伦理问题探讨与行业规范建设研究报告
- 食品安全总监和安全员的职责
- 2026-2030中国高纯氧化钇行业发展态势及投资动态预测报告
- 安全继电器工作原理培训课件
- 2026三氯化铁化学品安全技术说明书
- 2026年4月自考00539中国古代文学史(二)试题及答案含评分参考
评论
0/150
提交评论