14 堆与拷贝构造函数 2

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

2020年1月24日3时43分C++程序设计教程(修订版)第十四章堆与拷贝构造函数清华大学出版社钱能2020年1月24日3时43分第十四章堆与拷贝构造函数关于堆需要new和delete的原因分配堆对象拷贝构造函数默认拷贝构造函数浅拷贝与深拷贝临时对象无名对象构造函数用于类型转换2020年1月24日3时43分1、关于堆全局变量、静态数据、常量存放在全局数据区,所有类成员函数和非成员函数代码存放在代码区,为运行函数而分配的局部变量、函数参数、返回数据、返回地址等存放在栈区,余下的空间都被作为堆区。堆内碎块:堆区内众多不连续的内存小块动态分配内存应注意:及时释放内存,避免堆区碎块2020年1月24日3时43分2、需要new和delete的原因n在C++用new代替malloc()函数的一个原因是,它在分配空间的时候不能调用构造函数。类对象的建立是分配空间、构造结构以及初始化的三位一体,他们统一由构造函数来完成。n例如,下面的代码用malloc()分配对象空间:nclassTdaten{public:nTdate();nSetDate(intm=1,intd=1,inty=1998);n2020年1月24日3时43分nprotected:nintmonth;nintday;nintyear;n};nTdate::Tdate()n{nmonth=1;2、需要new和delete的原因2020年1月24日3时43分nday=1;nyear=1;n};nvoidTdate::SetDate(intm,intd,inty)n{nif(m0&&m13)nmonth=m;nif(d0&&d32)nday=y;nif(y0&&y3000)nyear=y;n}2、需要new和delete的原因2020年1月24日3时43分voidfn(){Tdate*pD;//仅仅是个指针,没有产生对象pD=(Tdate*)malloc(sizeofTdate);//并不调用构造函数//…free(pD);//并不调用析构函数}此时pD是一个含有类对象空间,对应的对象空间中的值不确定。为此,内存分配之后再进行初始化。2、需要new和delete的原因2020年1月24日3时43分n例如,下面的代码描述用malloc()来进行对象的创建过程:nvoidfn()n{nTdate*pD;npD=(Tdate*)malloc(sizeofTdate);npD-SetDate();//设置Tdate值n//…nfree(pD);n}n从根本上说,不是类对象的创建,因为它绕过了构造函数。2、需要new和delete的原因2020年1月24日3时43分3分配堆对象n堆对象的作用域:文件n用new分配的对象,在用delete释放前,一直占有堆空间,不管能否访问得到。n堆对象创建时(new)自动调用构造函数,释放时(delete)自动调用析构函数n分配堆对象:n形式1:指针标识符=new类型标识符;n形式2:指针标识符=new类型标识符(初始化值);n形式3:指针标识符=new类型标识符[数组维数];//对于数组,不能初始化n释放堆对象:n形式1:delete指针标识符;n形式2:delete[]指针标识符;//释放堆数组2020年1月24日3时43分3分配堆对象n看一下下面代码与前面的代码做一比较:nvoidfn()n{nTdate*pS;npS=newTdate;//分配堆空间并构造它n//…ndeletepS;//先析构,然后将空间返还给堆n}n构造函数可以有参数,所以跟在new后面的类类型也可以跟参数。n例如下面的代码,new后面的类型必须跟参数:2020年1月24日3时43分3分配堆对象nclassTdaten{npublic:nTdate(intm,intd,inty);nprotected:nintmonth;nintday;nintyear;n};nTdate::Tdate(intm,intd,inty)n{2020年1月24日3时43分if(m0&&m13)month=m;if(d0&&d32)day=d;if(y0&&y3000)year=y;}voidfn(){Tdate*pD;pD=newTdate(1,1,1998);3分配堆对象2020年1月24日3时43分//…delete(pD);}“pD=newTdate(1,1,1998);”这一名,使new去调用了构造函数Tdate(int,int,int),new是根据参数匹配的原则来调用构造函数的。如果上一句写成:pD=newTdate;则由于Tdate类没有默认构造函数而使该语句报错。从堆中还可以分配对象数组。例如,下面的代码分配了参数给定的对象个数,并在函数结束时,予以返还:3分配堆对象2020年1月24日3时43分classStudent{public:Student(char*pName=“noname”){strncpy(name,pName,sizeof(name));name[sizeof(name)-1]=“\0”;}protected:charname[40];};3分配堆对象2020年1月24日3时43分voidfn(intnoOfObjects){Student*pS=newStudent[noOfObjects];//…delete[]pS;}分配过程将激发noOfObjects次构造函数的调用,从0~noOfObjects-1。调用构造函数的顺序依次为pS[0],pS[1],…ps[noOfObjects-1]。由于分配数组时,new的格式是类型后面跟{元素个数},不能再跟构造函数参数。如果该类没有默认构造函数,则不能分配对象数组。3分配堆对象2020年1月24日3时43分4、拷贝构造函数拷贝构造函数:特殊的构造函数作用:当用一个已存在的对象构造一个新的对象时,系统不自动调用构造函数、而是自动调用拷贝构造函数;创建新的对象,并把对象的数据成员值拷贝给新的对象(在编译时进行)。拷贝构造函数声明的一般形式:类名(const类名&形参对象名);拷贝构造函数定义的一般形式:类名::类名(const类名&形参对象名){……}2020年1月24日3时43分4、拷贝构造函数拷贝构造函数被调用的3种情况:1.新建对象:classA{…};voidmain(){Aa;Aa1(a);}2.实参传递给形参时:…voidf(Aa){…}voidmain(){Aa;f(a);}3.函数返回时:…Afn(){Aa;…returna;}voidmain(){f(fn());}2020年1月24日3时43分4、拷贝构造函数n可用一个对象去构造另一个对象,或者说,用另一个对象值初始化一个新构造的对象,例如:nStudents1(“Jenny”);nStudents2=s1;//用s1的值去初始化s2n对象作为函数参数传递时,也要涉及对象的拷贝,例如:nvoidfn(Studentfs)n{n//…n}2020年1月24日3时43分voidmain(){Studentms;fn(ms);}函数fn()的参数传递的方式是传值,参数类型是Student,调用时,实参ms传给了形参fs,ms在传递的过程中是不会改变的,形参fs是ms的一个拷贝,形参fs用ms的值进行构造。这时候,调用构造函数Student(char*)就不合适,新的构造函数的参数应是Student&,也就是:Student(Student&s);4、拷贝构造函数2020年1月24日3时43分4、拷贝构造函数#includeiostream.h#includestring.hclassStudent{public:Student(char*pName=noname,intssId=0){strncpy(name,pName,40);name[39]='\0';id=ssId;coutConstructingnewstudentpNameendl;}Student(Student&s)//拷贝构造函数{2020年1月24日3时43分coutConstructingcopyofs.nameendl;strcpy(name,copyof);strcat(name,s.name);id=s.id;}~Student(){coutDestructingnameendl;}protected:charname[40];intid;};4、拷贝构造函数2020年1月24日3时43分voidfn(Students){coutInfunctionfn()\n;}voidmain(){Studentrandy(Randy,1234);coutCallingfn()\n;fn(randy);coutReturnedfromfn()\n;}4、拷贝构造函数2020年1月24日3时43分5、默认拷贝构造函数如果不定义拷贝构造函数,系统自动为类提供一个缺省的拷贝构造函数(逐一拷贝数据成员)。一般情况下,不必定义拷贝构造函数。但是,如果构造函数中存在动态分配,则必须定义拷贝构造函数例:#includeiostream.h#includestdio.hclassA{private:int*p;public:A(inti){p=newint(i);coutnew...endl;}~A(){coutdeletep...endl;deletep;}};intmain(){Aa1(5);//调用构造函数Aa2(a1);//调用系统提供的缺省的拷贝构造函数return0;/*调用析构函数两次,一个值为5的int型对象被删除两次,在运行时会产生问题*/}/*执行结果:new...deletep...deletep...执行到此,出错:指针p已经被释放*/2020年1月24日3时43分//定义拷贝构造函数来解决上述问题#includeiostream.h#includestdio.hclassA{private:int*p;public:A(inti){p=newint(i);coutnew...endl;}A(constA&r){p=newint(*r.p);coutcopy_consructor...endl;}~A(){deletep;coutdeletep...endl;};};intmain(){Aa1(5);Aa2(a1);//调用自定义的拷贝构造函数return0;}/*执行结果:new...copy_constructor...deletep...deletep...*/5、默认拷贝构造函数2020年1月24日3时43分6、浅拷贝与深拷贝n在默认拷贝构造函数中,拷贝的策略是逐个成员依次拷贝。但是,一个类可能会拥有资源,当其构造函数分配了一个资源的时候,会发生什么呢?如果拷贝构造函数简单地制作了一个该资源的拷贝,而不对它本身分配,就面临一个麻烦的局面:两个对象都拥有同一个资源。当对象析构时,该资源将经历两次资源返还。n2020年1月24日3时43分调用默认拷贝构造函数,使得p2与p1完全一样,并没有新分配堆空间给p2,见下图:6、浅拷贝与深拷贝2020年1月24日3时43分创建p2时,对象p1被复制给了p2,但资源并未复制,因此,p1和p2指向同一个资源,这称为浅拷贝。当一个对象创建时,分配了资源,这时,就需要定义自己的拷贝构造函数,使之不但拷贝成员,也拷贝资源。6、浅拷贝与深拷贝2020年1月24日3时43分拷贝构造函数中,不但复制了对象空间,也复制资源,见下图:6、浅拷贝与深拷贝2020年1月24日3时43分创建p2时,对象p1被复制给了p2,同时资源也作了复制,因此,p1和p2指向不同的资源,这称为深拷贝。如果你的类

1 / 32
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功