[经济学]第5章堆与复制构造函数课件.ppt

上传人:牧羊曲112 文档编号:4012525 上传时间:2023-04-01 格式:PPT 页数:54 大小:333KB
返回 下载 相关 举报
[经济学]第5章堆与复制构造函数课件.ppt_第1页
第1页 / 共54页
[经济学]第5章堆与复制构造函数课件.ppt_第2页
第2页 / 共54页
[经济学]第5章堆与复制构造函数课件.ppt_第3页
第3页 / 共54页
[经济学]第5章堆与复制构造函数课件.ppt_第4页
第4页 / 共54页
[经济学]第5章堆与复制构造函数课件.ppt_第5页
第5页 / 共54页
点击查看更多>>
资源描述

《[经济学]第5章堆与复制构造函数课件.ppt》由会员分享,可在线阅读,更多相关《[经济学]第5章堆与复制构造函数课件.ppt(54页珍藏版)》请在三一办公上搜索。

1、C+面向对象程序设计教学内容,第1章 C+概述第2章 类和对象第3章 面向对象程序设计概述第4章 进一步学习类和对象第5章 堆与复制构造函数第6章 继承性:派生类第7章 运算符重载第8章 虚函数和多态性第9章 模板第10章 类库和C+的标准模板库STL第11章 输入输出流第12章 异常处理,第5章 堆与复制构造函数,5.1 堆5.2 需要new和delete的原因5.3 默认的复制构造函数5.4 自定义复制构造函数,5.1 堆 Heap,堆是按动态方式分配的内存区域。在程序中需要空间存放数据时,就申请动态内存单元,使用完毕后释放动态内存单元。这种动态内存分配方式能够显著地提高内存的利用率。,C

2、+程序的内存布局,Stack 栈,Heap 堆,Global data 全局数据区,程序代码区program code,代码区存放程序的代码(可执行指令);,全局数据区存放全局变量、静态变量、常量。,固定存储区域,栈是存放程序中的所有动态局部变量、函数参数、函数返回值等信息的一块内存区域。,在固定存储区域与堆栈之间的自由区域称为堆,栈 Stack,栈的内存管理严格遵循后进先出(LIFO:Last in,First Out)的顺序,即释放栈中对象所占内存时的顺序刚好与给这些对象分配栈中内存时的顺序相反,这一点正是实现函数调用所需要的。从栈中分配内存效率特别高,对栈的充分利用是C/C+编译程序能产

3、生优质高效代码的原因之一。,动态内存分配,堆的内存是以动态分配方式管理的。所谓动态分配的内存是在程序运行期间获得的。动态存储分配方式允许我们的程序可以在执行期间根据实际的需要存放的数据量来申请合适数量的内存单元。这种动态分配方式不但能够提高内存的利用率,而且对于链表和二叉树等动态数据结构特别有用。,动态内存申请和释放,用函数malloc()分配的动态内存必须用函数free()释放;用new申请的动态内存必须用delete 释放。因为在C+程序中,从堆中获取的内存单元不会被自动释放,因此必须使用函数free()或者用delete释放这种内存。如果从堆中获取的内存在使用完后没有被释放,这部分内存在

4、程序结束之前会一直被占用,这种情况被称为“内存泄漏”。,5.2 需要new和delete的原因,5.2.1 需要new和delete的原因5.2.2 在堆上创建对象,5.2.1 需要new和delete的原因,对自定义的类类型,使用函数malloc()给对象分配动态空间时不能自动调用构造函数;使用函数free()释放对象所占用的动态空间时也不能调用析构函数。C+语言创建了new和delete两个运算符来满足面向对象的新特性,在C+语言程序中,我们应该使用new和delete来创建和销毁类的对象。,5.2.2 在堆上创建对象,使用new运算符在堆上创建对象时能够自动调用构造函数进行初始化;使用

5、delete运算符释放对象占用的动态内存时能够自动调用该对象的析构函数进行善后处理。,【例5.1】在堆上创建对象,#include using namespace std;class Square int side;public:Square(int x)side=x;coutConstructiongn;Square()coutDestructiongn;void display()cout side n;,【例5.1】在堆上创建对象(续),int main()Square*ps=new Square(10);/分配堆内存并调用构造函数初始化 ps-display();delete ps;/

6、自动调用析构函数,然后释放堆内存 return 0;该程序运行后的输出结果如下:Constructiong10Destructiong,【例5.2】传值调用例子,class Square int side;public:Square(int x)side=x;coutConstructiongn;Square()coutDestructiongn;void display()cout side n;void f(Square ob)/对象作为函数参数 ob.display();,【例5.2】传值调用例子(续),int main()Square s(10);f(s);/对象s以传值方式传送给临时

7、对象obs.display();return 0;该程序运行后的输出结果如下:Constructiong10DestructiongDestructiong10,对象的副本,当一个对象被作为参数传递给函数时,同时也创建了该对象的副本这个副本将成为函数的参数。也就是说,创建了一个新的对象。当函数结束时,作为函数的实际参数的副本将被销毁。也就是说,一个对象被销毁了,两个问题,第一个问题:在创建对象的副本时是否调用了构造函数?第二个问题:当销毁对象的副本时是否调用了析构函数?,第一个问题的答案,首先,在调用函数的时候,程序创建了一个对象的副本作为形式参数,此时普通的构造函数(normal const

8、ructor)并没有被调用,而是复制构造函数(copy constructor)被调用。,为什么需要复制构造函数,由于普通的构造函数通常用于初始化对象的某些成员,因此就不能调用普通构造函数创建对象的副本,因为这样产生的对象可能与现有的对象不完全相同。当把一个对象传递给函数时,我们需要使用的是对象的当前状态,而不是初始状态,5.3 默认的复制构造函数,复制构造函数定义了如何创建一个对象的副本。如果一个类中没有显式地定义类的复制构造函数,那么C+将提供一个默认的复制构造函数(default copy constructor)。默认的复制构造函数将以按位复制的形式创建一个对象的副本,即创建一个与原对

9、象一模一样的副本。,第二个问题的答案,当函数结束时,由于作为参数的对象副本超出了作用域,因此它将被销毁,从而调用了析构函数。,结论,当创建一个对象的副本作为函数的参数时,复制构造函数被调用。当对象的副本被销毁时(通常因为函数返回而超出作用域),析构函数被调用。,自定义复制构造函数的例子,Square(const Square,如果程序员自定义了一个复制构造函数,编译时将不会产生默认的复制构造函数。,【例5.2】的另一种版本,class Square int side;public:Square(int x)side=x;coutConstructiongn;Square(const Squar

10、e,【例5.2】的另一种版本(续),int main()Square s(10);f(s);/对象s以传值方式传送给临时对象obs.display();return 0;该程序运行后的输出结果如下:ConstructiongCopy Constructiong10Destructiong10Destructiong,自定义复制构造函数,在很多情况下,使用按位复制的方法来创建一个相同的对象副本是可行的,这时我们可以直接使用C+语言提供的默认复制构造函数。但是,在某些情况下使用默认的复制构造函数会出现问题(例如【例5.3】),这时我们需要自己创建复制构造函数。,【例5.3】,class my_st

11、ring char*s;public:my_string(char*str)s=new charstrlen(str)+1;coutAllocating room for sn;strcpy(s,str);my_string()if(s)delete s;cout Freeing sn;void show()cout s n;,【例5.3】(续),void display(my_string ob)ob.show();int main()my_string obj(Hello!);display(obj);obj.show();/这条语句输出的是垃圾数据return 0;,使用默认的复制构造函

12、数出现了问题,该程序运行后的输出结果如下:Allocating room for sHello!Freeing s(撤销对象obj的副本ob时调用析构函数)葺葺葺葺葺(这是输出的垃圾数据,不同的系统中输出的内容会不相同)Freeing s(撤销对象obj时调用析构函数),【例5.3】C+程序中的对象内存分布示意图,Hello,实参对象obj,形参对象ob,(a)从函数display()返回前,S,S,【例5.3】C+程序中的对象内存分布示意图,实参对象obj,(b)从函数display()返回后,S,解决这个问题的方法之一,void display(my_string 经过修改后,程序运行后的

13、输出结果如下:Allocating room for sHello!Hello!Freeing s,避免复制对象,初始化(initialization),(1)当一个对象副本被作为参数传递给函数时。my_string y;display(y);/y被作为参数传递给函数当一个对象被另一个对象显式地初始化时,例如在对象的声明中。my_string x=y;/对象y被显式地用来初始化对象x(3)当创建一个临时对象时(最常见的情况是作为函数的返回值)。y=func2();/y得到一个返回对象,5.4.2 复制构造函数与函数参数,当对象被作为参数传递给函数时,会产生该对象的一个副本。如果我们创建了自定义

14、的复制构造函数,那么这个自定义的复制构造函数将被调用来制作这个对象副本。,【例5.4】自定义的复制构造函数,#include#include using namespace std;class my_string char*s;public:my_string(char*str);/普通构造函数my_string(const my_string,【例5.4】(续1),my_string:my_string(char*str)/普通构造函数s=new charstrlen(str)+1;coutAllocating room for sn;strcpy(s,str);my_string:my_s

15、tring(const my_string,【例5.4】(续2),void display(my_string ob)ob.show();int main()my_string obj(Hello!);display(obj);obj.show();return 0;,【例5.4】(续3),该程序运行后的输出结果如下:Allocating room for sCopy constructor called.Hello!Freeing sHello!Freeing s,【例5.4】中C+程序的对象内存分布示意图,Hello,实参对象obj,形参对象ob,(a)从函数display()返回前,S,

16、S,Hello,【例5.4】C+程序中的对象内存分布示意图,实参对象obj,(b)从函数display()返回后,S,Hello,5.4.3 复制构造函数与初始化,当使用一个对象来初始化另一个对象时,也将调用复制构造函数,【例5.5】初始化对象时调用复制构造函数。,#include#include using namespace std;class my_string char*s;public:my_string(char*str);/普通构造函数my_string(const my_string,【例5.5】(续1),my_string:my_string(char*str)/普通构造函数

17、s=new charstrlen(str)+1;coutNormal constructor called.n;strcpy(s,str);my_string:my_string(const my_string,【例5.5】(续2),int main()my_string obj(Hello!);/调用普通构造函数my_string ob1(obj);/调用复制构造函数my_string ob2=ob1;/调用复制构造函数ob1.show();ob2.show();return 0;,【例5.5】(续3),该程序运行后的输出结果如下:Normal constructor called.Copy

18、 constructor called.Copy constructor called.Hello!Hello!Freeing sFreeing sFreeing s,复制构造函数不会影响赋值运算,记住,复制构造函数只有在初始化对象时才被调用。例如,下面的代码将不会调用在前面程序中定义的复制构造函数:my_string s1(“Hello”),s2(“world”);/s1=s2;,赋值运算,5.4.4 在返回对象时使用复制构造函数,当函数返回对象时,函数创建了一个临时对象来保存要返回的值,而函数所返回的对象实际上是这个临时对象。在对象的值被返回之后,临时对象将被销毁。在某些情况下,这会引起不

19、可预料的错误。,【例5.6】函数返回对象,#include#include using namespace std;class my_string char*s;public:my_string(char*str);/普通构造函数my_string()if(s)delete s;cout Freeing sn;void show()cout s n;,【例5.6】(续1),my_string:my_string(char*str)/普通构造函数s=new charstrlen(str)+1;coutinstr;my_string ob(instr);/调用普通构造函数return ob;,【例

20、5.6】(续2),int main()my_string obj=input();/调用默认的复制构造函数/在函数input()中将调用普通构造函数obj.show();/输出的是垃圾数据return 0;该程序运行后的输出结果如下:Enter a string:HelloNormal constructor called.Freeing s葺葺葺葺葺葺葺葺葺葺輛(这是输出的垃圾数据,不同的系统中输出的内容会不相同),【例5.7】使用自定义的复制构造函数避免错误,#include#include using namespace std;class my_string char*s;public

21、:my_string(char*str);/普通构造函数my_string(const my_string,【例5.7】(续1),my_string:my_string(char*str)/普通构造函数s=new charstrlen(str)+1;coutNormal constructor called.n;strcpy(s,str);/自定义的复制构造函数my_string:my_string(const my_string,【例5.7】(续2),my_string input()/返回一个my_string类型的对象char instr80;coutinstr;my_string ob(instr);/调用普通构造函数return ob;/调用复制构造函数int main()my_string obj=input();/调用复制构造函数/在函数input()中将调用普通构造函数obj.show();return 0;,【例5.7】(续3),该程序运行后的输出结果如下:Enter a string:HelloNormal constructor called.Copy constructor called.Freeing sHelloFreeing s,一个经验,如果你的类需要析构函数来释放资源,则它也需要一个自定义的复制构造函数。,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号