动态内存管理技术课件.ppt

上传人:小飞机 文档编号:4000513 上传时间:2023-03-31 格式:PPT 页数:105 大小:353.50KB
返回 下载 相关 举报
动态内存管理技术课件.ppt_第1页
第1页 / 共105页
动态内存管理技术课件.ppt_第2页
第2页 / 共105页
动态内存管理技术课件.ppt_第3页
第3页 / 共105页
动态内存管理技术课件.ppt_第4页
第4页 / 共105页
动态内存管理技术课件.ppt_第5页
第5页 / 共105页
点击查看更多>>
资源描述

《动态内存管理技术课件.ppt》由会员分享,可在线阅读,更多相关《动态内存管理技术课件.ppt(105页珍藏版)》请在三一办公上搜索。

1、2023/3/31,1,动态内存管理技术,C+常见问题,1,3,2,Const的使用,内联函数的使用,使用引用,4,类型转换,5,异常处理,6,命名空间,7,内容提要,2023/3/31,2,6.1内联函数的使用,6.1.1 内联函数引入的原因 调用函数时,要跳到函数的起始地址去执行,执行完函数的代码后,再返回到调用点继续执行。这种跳转操作需要保存现场及返回地址;返回时,又需要恢复现场。函数调用需要一定的时间开销和空间开销,这会影响程序的执行效率。,外联开销,2023/3/31,3,对内联函数,编译器会在程序出现内联函数调用的地方用函数体直接进行替换。目标程序将不存在调用问题,也就不会产生跳转

2、的问题,自然就不存在函数调用所需要的时间和空间开销。在编译时用函数代码替换到调用处,如果函数代码长度比较长,超过了跳转语句所占空间的长度,这自然会增加整个程序的代码量,进而增加了空间的开销。,实质是用空间换时间。,6.1内联函数的使用,2023/3/31,4,6.1.2 内联函数的定义 例6_1:smain6_1.cpp/1、全局函数定义为内联函数inline double Show1(double dX)return dX*dX;,6.1内联函数的使用,2023/3/31,5,/2、在类声明体内实现的函数不用inline关键字,自动是内联函数。double Show2(double dX)c

3、onstreturn dX*dX;,6.1内联函数的使用,2023/3/31,6,/3、在函数声明处可加可不加inline关键字。double Show3(double dX)const;/4、但在实现处必须加inline关键字inline double CPerson:Show3(double dX)constreturn dX*dX;,6.1内联函数的使用,2023/3/31,7,内联函数使用时注意以下几点。1、在内联函数中不允许用循环语句、开关语句和递归调用语句等。2、内联函数的定义必须出现在第一次调用内联函数之前。3、在类内部定义并实现的成员函数自动是内联函数,但在类内定义成员函数时,

4、如果使用了for()、while()、do while()、switch()等语句,该成员函数会自动转为非内联函数。4、内联函数中不能够有静态数据。5、内联函数中不能够有数组说明。,6.1内联函数的使用,2023/3/31,8,6.1.3 内联和非内联函数的选择使用内联函数时间开销的减少,以空间的消耗为代价,而且执行效率的提高也不是绝对的。在决定使用内联函数时要进行适当的取舍。不是所有的函数都适合采用内联函数。构造函数和析构函数常常就不适合内联,这是因为构造函数和析构函数在编译时,往往会被编译器附加一大堆代码。,全部内联可以吗?,6.1内联函数的使用,2023/3/31,9,看看CStuden

5、t类的构造函数:例6_2:构造函数、析构函数往往不适合作内联函数。smain6_2.cpp,构造函数、析构函数最好不要内联。,6.1内联函数的使用,2023/3/31,10,CStudent()构造函数看起来是空的,但它可能含有相当多的代码(与编译器的设计有关)。CStudent的构造函数,需要构建4000字节的空间,并且还需要按照基类的要求初始化这些空间,因此它包含了一个复杂的创建过程。构造函数和析构函数最好都在类内声明,类外实现。一般来说,实际编程时最好是不要内联任何函数,除非函数确实很小很简单。,不要内联任何函数,6.1内联函数的使用,2023/3/31,11,6.2 const的使用,

6、const意味着分配了一块不可改变的内存。用const定义或说明常类型量时必须初始化。常类型量,也就是常量。const更重要的用途则在于说明函数参数以及函数返回类型。,函数参数,2023/3/31,12,const主要有以下几种用法:1、const说明值常量。2、const说明指针。3、const说明函数参数及其返回值。4、const说明类中的常量和成员函数。,6.2 const的使用,2023/3/31,13,6.2.1 const说明值常量 1、说明符号常量,表明符号代表的是一个常量,说明格式如下:double const PI=12;const double PI=12;2、说明数组常量

7、,说明格式如下:const int I_ARRAY=3,2,1;3、说明对象常量,说明格式如下:const CInline1 oC1,oC2;在一排逗号隔开的变量中,只需要一个const就行了,在前、在后定义都可以。,6.2 const的使用,2023/3/31,14,const与C语言#define的差别:#define PI 3.14无参宏不是符号常量,它没有数据类型,没有值,在内存中不分配地址。它是在预处理时作宏替换,不作类型检查。而const定义的常量是符号常量,有数据类型,有值,且其值不可改变,在内存中有地址,编译时要作类型检查。,6.2 const的使用,2023/3/31,15,

8、6.2.2 const与指针 指针涉及三个东西:一个是指针变量本身;另一个是指针指针变量中所存放的值;再一个就是指针所指向的对象。,6.2 const的使用,2023/3/31,16,指针其实就是地址,它代表的是指针变量中存放的地址值;而指针变量是指用来存放指针的一个变量,它用来存放地址;注意:指针变量本身也有地址,有存储单元;指针所指向的对象是指那片空间所“存放的东西”。指针变量本身的地址,与指针变量所指向的对象的存储地址是两个不同的地址。,6.2 const的使用,2023/3/31,17,6.2 const的使用,2023/3/31,18,const和指针变量的典型组合有以下三种情况:1

9、、指向常量的指针 const int*p;2、常指针 char*const pc=a;3、指向常量的常指针const char*const pc=a;,6.2 const的使用,2023/3/31,19,const int*p;char*const pc=a;const char*const pc=a;画一条垂直线穿过指针声明中的星号(*)位置。如果const出现在线的左边,则指针指向的对象为常量;如果const出现在线的右边,则指针本身为常量;如果const在线的两边都出现,则二者都是常量,是指向常量的常指针。,6.2 const的使用,2023/3/31,20,1 指向常量的指针 指针指向

10、一个不可改变的量。指向常量。在声明时可以不初始化。该指针可以指向这个常量,也可以指向另一个常量。指针变量里的指针值是可以改变的;但某一个具体指针值(即:某个指针)所指向的那个对象则是不可以改变的。,对象不可改变。,6.2 const的使用,2023/3/31,21,它也可以指向变量;从指针角度而言,所指向的是一个常量,通过该指针不能修改它所指向的对象;该指针变量可以存放常量地址也可以存放变量地址。,对象不可改变。,6.2 const的使用,2023/3/31,22,const char*pc=ABCD;pc3=a;/不可以。pc=“EFGH”;/可以/step是一个指向常量的指针数组 cons

11、t char*step3=left,right,top;step2=skip;/可以。step21=i;/不可以。,6.2 const的使用,2023/3/31,23,2 常指针*const=;指针变量里面存放的是个常量,称为常指针,指针变量里面装的是一个固定值,在定义时必须初始化。常指针一旦初始化后,不能够再指向其他内存单元。通过常指针可以修改它所指向的内存单元的内容,也就是修改常指针所指向的对象。,6.2 const的使用,2023/3/31,24,char*const pc=ABCD;/常指针pc3=a;/可以。pc=EFGH;/不可以/step是一个常指针数组 char*const s

12、tep3=left,right,top;step2=skip;/不可以。step21=i;/可以。,6.2 const的使用,2023/3/31,25,3 指向常量的常指针 指针本身及指针所指向的对象都不可改变。二者都要声明为 const。在声明时必须初始化。指针本身及其指向的对象均不能改变。const char*const pc=asdf;pc3=a;/不可以。pc=ABCD;/不可以。,6.2 const的使用,2023/3/31,26,6.2.3 const说明函数参数和返回值是const最重要的应用。在值传递场合不必用const。用const去修饰用指针和引用传递的函数参数,是安全的。

13、const修饰函数的返回值。,安全的,6.2 const的使用,2023/3/31,27,void Func(const char*lpszChar);void Func(char const*lpszChar);/以上二者等价。/参数是对象的常引用,返回值也是一个对象的常引用。const CStudent 对返回值使用const也有可能提高函数的安全性和效率。,6.2 const的使用,2023/3/31,28,6.2.4 类中的const在类中,const的使用也比较广。它可以用于修饰类中的成员函数和成员数据。,主要应用,6.2 const的使用,2023/3/31,29,1 常成员函数

14、不可以改变对象数据成员的函数。在一般成员函数后面加上const即可。1、用const关键词修饰一个成员函数,说明该成员函数是“只读”的。2、只有常成员函数可以操作常对象,常对象只能调用常成员函数(const成员函数)。3、一般对象不但可以调用一般成员函数,也可以调用常成员函数。,6.2 const的使用,2023/3/31,30,例6_3:常对象只能够调用常成员函数,不能够调用一般成员函数,而一般对象可以调用所有的成员函数。s6_3sclass6_3_T.h s6_3sclass6_3_point.cpp s6_3smain6_3.cpp运行结果,一般成员函数调用:1 2一般成员函数调用:2

15、3 常成员函数调用:3 3,6.2 const的使用,2023/3/31,31,例子表明:仅在const方面有不同的成员函数是可以重载的,编译器能够正确区分这两个函数。而对调用对象来说,一般对象优先调用重载的一般函数,其次才是const函数;常对象(const对象)则只能够调用const函数,不能够调用一般函数。对于一般对象,当调用一般成员函数不成功时,系统自动会转入对常成员函数的调用。,6.2 const的使用,2023/3/31,32,2 常数据成员若一个数据成员的前面用了关键词const修饰符,则该数据成员为常数据成员。const 型的数据必须初始化,且不能修改,它是常数据成员。常数据成

16、员的初始化方式在定义对象时,通过在构造函数后面加上成员初始化列表来完成的。smain6_4.cpp,6.2 const的使用,2023/3/31,33,class CPoint/CPoint类private:int m_x;/x坐标const int m_y;/y坐标,常数据成员static const int m_z;/z坐标,静态常数据成员;const int CPoint:m_z=0;/静态数据成员的初始化方式。/构造函数,m_y采用了初始化表的方式初始化。CPoint:CPoint(int x,int y):m_y(y)m_x=x;/正确。/m_y=y;/错。,6.2 const的使用

17、,2023/3/31,34,在CPoint中设置了空间坐标中的m_x、m_y和m_z坐标。分别把它们设置成一般数据成员、常数据成员和静态常数据成员。由于类型不同,它们所采用的初始化方式是很不一样的。在设计构造函数时,最好对一般成员数据也采用初始化表的方式进行初始化。这样效率最高。,6.2 const的使用,2023/3/31,35,3 常对象常对象就是其值不可改变的对象。它是只可以取值不可以修改的对象。常对象一旦建立并初始化以后,只可以通过常成员函数来访问它。const CPoint ocCPoint3(3,3);,6.2 const的使用,2023/3/31,36,6.3 动态内存管理技术,

18、动态对象就是程序在运行过程中在动态内存中建立的对象。这类对象需要用户自己使用new运算符创建,使用delete运算符释放,需要用户自己管理。,堆,栈,静态内存,2023/3/31,37,6.3.1 内存的几种分配方式计算机内存通常具有三种组织方式:堆、栈和静态内存。1、在静态存储区中分配。静态存储区中的变量空间在编译时分配。静态内存区中的变量在程序的整个运行期间都存在。其生命周期贯穿整个程序的运行周期。例如全局变量,static变量等在这个区间分配。,6.3 动态内存管理技术,2023/3/31,38,2、在栈上分配自动变量等局部变量在栈上分配存储空间。函数内的局部变量在栈上分配存储单元。它的

19、生命周期与函数的执行时间相同。当函数执行结束时这些存储单元会被自动释放,其生命周期也就完结了。,6.3 动态内存管理技术,2023/3/31,39,3、在堆上分配,亦称动态内存分配。动态内存由程序员自己负责管理。运行时用new申请内存。程序员负责在不需要时用delete释放内存。,比拉登还麻烦。容易出问题。,6.3 动态内存管理技术,2023/3/31,40,6.3.2 使用new和delete分配和释放动态内存malloc()与free()标准库函数;new/delete运算符(不是库函数)。malloc()/free()功能有限。用户自定义类型在创建对象时要执行构造函数,对象在消亡之前要执

20、行析构函数。而且是自动执行的。所以,C+使用能完成动态内存分配和初始化工作的运算符new,及能完成清理与释放内存工作的运算符delete来管理动态内存。,6.3 动态内存管理技术,2023/3/31,41,6.3.2.1 new运算符 new用来动态地分配存储空间。它能够自动计算要分配的存储空间大小并能返回正确的指针类型。若返回值为NULL,则表示动态内存分配不成功。在用new申请动态内存后,一定要判断动态内存是否分配成功。,if(p!=NULL),6.3 动态内存管理技术,2023/3/31,42,int*pInt1,*pInt2;pInt1=new int;/new 运算返回一整数单元的地

21、址pInt2=new int(200);/new一个int单元,并将其初始化为200。int*pInt3,(*pInt4)3;pInt3=new int10;/new 运算返回一整数单元的地址,有10个这样的单元。pInt4=new int23;/new 运算返回每行为3个元素的行地址CPoint*poCPoint;poCPoint=new CPoint(10,10);/new 运算返回对象的地址,并在动态内存中创建了一个对象。,6.3 动态内存管理技术,2023/3/31,43,使用new运算符创建数组时,不能为该数组指定初值。执行“CPoint poCPoint=new CPoint(10

22、,10);”语句时,得到了两个东西:一个是对象指针变量,另一个是在动态内存空间中的对象,而指针变量中的值,就是这个动态内存空间中的对象的首地址,也就是动态内存地址。而且这个对象是没有名字的,只有通过指针进行访问。,6.3 动态内存管理技术,2023/3/31,44,2 delete运算符delete释放new所分配的存储空间。是存放了要释放的存储空间地址的指针变量名字。释放new所创建数组时,使用“delete;”的形式。delete只能用来释放用new申请得的动态内存空间,而且一次new只能够对应一次delete。,配对使用,6.3 动态内存管理技术,2023/3/31,45,delete

23、pInt1;delete pInt2;delete pInt3;/释放用new申请的数组空间/释放由new申请的数组空间,对多维空间的释放格式与一维空间相同。delete pInt4;delete poCPoint;/释放一个对象,6.3 动态内存管理技术,2023/3/31,46,注意:delete后,指针所指向的内存空间就释放了,但指针变量的值并没有改变。指针还指向原来的内存空间。所以,delete以后,应将指针变量的值设置成NULL,让它不再指向原来的内存空间,以免误操作。例6_5:显示了new和delete的基本使用 smain6_5.cpp,6.3 动态内存管理技术,2023/3/3

24、1,47,CPoint*poCPoint;/创建对象时,自动调用构造函数。poCPoint=new CPointARRAY_SIZE;/判断动态内存申请是否成功。if(poCPoint=NULL)cout 动态内存分配失败。n;exit(0);/内存申请失败,则退出.,6.3 动态内存管理技术,2023/3/31,48,delete poCPoint;/释放对象数组。/动态内存释放后,让poCPoint指针指向NULL。否则,它依然指向原动态空间。poCPoint=NULL;,其内容为垃圾,6.3 动态内存管理技术,2023/3/31,49,在例6_5中,数组中的5个对象其坐标都是(0,0,0

25、)。这是由于给数组分配空间时,不能进行初始化(声明时不能够带参数)。也就是说:new一个对象数组时是不能够带参数的。缺省的参数则可以,构造函数缺省值为(0,0,0)。,6.3 动态内存管理技术,2023/3/31,50,对象数组的对象初始化有两种方法:1、在类中不定义构造函数,而定义一个成员函数专门用来完成初始化。2、在所定义的类中增加不带参数或带缺省参数的构造函数。,6.3 动态内存管理技术,2023/3/31,51,poCPoint=new CPointARRAY_SIZE;“表明对应着:delete poCPoint;;而:CTemp*poCTemp=new CTemp(5);”对应着:

26、delete poCTemp;。如果new和delete形式不对应,结果将不可预测。,6.3 动态内存管理技术,2023/3/31,52,3 new和delete的重载 new和delete可以在类内重载,也可以在类外重载。类外重载就是全局函数重载,它会覆盖系统预定义的new和delete功能,。类内:new和delete只能重载为成员函数,不能重载为友元函数。而且,无论是否使用关键字static进行修饰,重载了的new和delete均为类的静态成员函数。,6.3 动态内存管理技术,2023/3/31,53,void*operator new(size_t size)void*p=malloc

27、(size);return(p);void operator delete(void*p);free(p);,6.3 动态内存管理技术,2023/3/31,54,6.3.3 常见的动态内存错误编译器不能自动发现动态内存错误。在程序运行时才能捕捉到。常见的动态内存错误有:1、使用未分配成功的动态内存空间。在使用动态内存之前必须检查指针是否为NULL:函数入口处:assert(p!=NULL)assert()函数对应的头文件为:assert.h参考C语言程序设计(C99),清华大学出版社申请时用:if(p=NULL)或if(p!=NULL)进行检查.,6.3 动态内存管理技术,2023/3/31,

28、55,2、使用未经初始化的动态内存空间。内存的缺省初值可能是一个不确定的值,所以,任何内存空间的使用都应该立足于自己初始化。尽量不要使用系统缺省的初值。3、超过了内存空间的边界使用 指针指向了不正确的类型时,会出错;数组的操作也很容易越界;越界后就会操作到不正确的内存单元.,6.3 动态内存管理技术,2023/3/31,56,4、内存泄露。是一种严重的内存错误,极不容易发现。程序长期运行的话,最终会导致内存耗尽。由于new和delete使用不配对new创建的动态空间,没有用delete释放等。5、使用已经释放了的内存空间。用free或delete释放内存以后,指针变量p的值并没有改变,它依然指

29、向原来new的那一片内存空间。释放内存以后,一定要将指针变量的值置成NULL。另外,在传递内存指针时要避免传递栈内存(存储临时变量的内存)。,6.3 动态内存管理技术,2023/3/31,57,6.3.4 指针和数组的对比数组名对应着一块内存,其地址与容量在生命期内保持不变,数组的内容则可以改变;指针变量(尤其void*)则是一个变量,它可以存放任意的地址值,它可以随时指向任意类型的内存块,所以指针远比数组灵活、危险。例6_7说明了指针和数组在使用上的一些差异。使用不慎将引发内存问题。smain6_7.cpp,6.3 动态内存管理技术,Oelloworldhellohello124,2023/

30、3/31,58,6.3.5 利用指针传递内存的方式 利用函数申请动态内存,可以使用指向指针的指针、指针引用以及函数返回指针来传递动态内存。例6_8:利用指向指针的指针、指针引用和函数返回指针传递内存。smain6_8.cpp,hello,world.hello,china.hello,四川大学计算机学院!,6.3 动态内存管理技术,2023/3/31,59,例6_9:利用指针传递内存时,注意不要传递栈内存,否则指针会指向垃圾。smain6_9.cpp 栈内存调用的结果为:,静态内存调用的结果为:hello world在例6_9中:GetString1的return语句返回了指向“栈内存”的指针

31、,它是错误的,因为该内存在函数结束时自动消失;而GetString2虽然返回了正确的结果,但它在设计概念上就已经错误了(因得到的是常量空间)。,6.3 动态内存管理技术,2023/3/31,60,以下两者本质上是不同的:char aChar=hello world;char*aChar=hello world;,动态数组初始化,6.3 动态内存管理技术,2023/3/31,61,6.3.6 delete的作用delete只是把指针所指向的内存给释放掉,它并没有使指针本身消失。delete以后,指针还是指向其原来指向的内存空间,指针变量的值并未改变。指针所指向的内存空间的内容已经释放掉了,其内容

32、变得毫无意义了,是垃圾,指针本身被悬挂了。此时用语句if(p!=NULL)进行防错处理是毫无意义的.,6.3 动态内存管理技术,2023/3/31,62,在例6_10中,安排了这种悬挂所带来的问题的示例程序。例6_10:delete对内存干了什么?smain6_10.cpp lpszChar和lpszChar2指向了同一片动态内存空间,delete以后,内存的内容毫无意义,但是lpszChar和lpszChar2指针变量的值并没有改变,还指向原来的位置。,6.3 动态内存管理技术,2023/3/31,63,lpszChar2:屯屯屯屯屯屯屯屯葺葺葺葺葺葺lpszChar:屯屯屯屯屯屯屯屯葺葺葺

33、葺葺葺lpszChar2:hello,China!lpszChar:hello,China!lpszChar2:葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺lpszChar:葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺lpszChar2:hello world。lpszChar:hello world。,6.3 动态内存管理技术,2023/3/31,64,预防“指针悬挂”和“野指针”(此处“指针悬挂”和“野指针”是同义词,都指指针指向不可用内存区域)从以下几点入手:1、声明指针时记住初始化,如不初始化就一定将其置成NULL.2、指针delete以后,一定记得将其置为NULL。3、当指针指向数组时,谨防指针操作越界.4

34、、避免指针指向一个已经自动消失的局部变量。,6.3 动态内存管理技术,2023/3/31,65,void Func(void)char*lpszChar=new char100;,/在退出函数时,动态内存会自动释放吗?,6.3 动态内存管理技术,2023/3/31,66,退出Func()的时候,lpszChar会自动消失;而动态内存却依然存在,没有释放,造成内存泄漏。1、指针消亡了,并不表示它所指向的动态内存会自动释放了。2、动态内存释放了,并不表示指向该动态内存的指针会消亡或自动变成NULL指针。,6.3 动态内存管理技术,2023/3/31,67,6.4使用引用,引用(/错误,引用是固定的

35、。riA是iA的同义词,它们表示同一个对象。,2023/3/31,68,riA是iA的一个引用;iA是一般变量,它是被引用的对象。定义引用类型变量时必须初始化。引用只能引用一个固定的对象。初始化以后,引用关系就不可改变。对对象或对引用的任何操作都会影响到被引用对象本身。一个引用可看作一个特殊类型的指针。,6.4使用引用,2023/3/31,69,1 指针引用C中,改变指针本身采用传指针地址的方式.void Func(int*ppInt);int iNum=47;int*pInt=/&表示取指针变量的地址,6.4使用引用,2023/3/31,70,利用指针引用传递指针值。,C+中,可以采用引用来

36、完成。函数参数变成指针的引用,用不着取得指针的地址。void NewMemory3(char*,6.4使用引用,2023/3/31,71,void Test3(void)char*lpszStr=NULL;NewMemory3(lpszStr,100);strcpy(lpszStr,hello,world.);cout lpszStr endl;delete lpszStr;,传递动态内存(用户定义函数),6.4使用引用,2023/3/31,72,6.4.1.2 使用引用的限制 使用引用时注意两点:1、引用必须初始化。2、引用在初始化中被绑定到某个对象上后,将只能永远绑定这个对象。,不可空引用

37、,6.4使用引用,2023/3/31,73,引用的效能:1、引用主要用于函数参数传递,解决大块数据或对象的传递效率和空间效率问题。2、用引用传递函数参数,不产生副本,提高传递效率,通过const的使用,保证了引用传递的安全性。3、引用与指针的区别:指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。指针使程序可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。引用固定,安全性高;指针可指向任何位置,危险。4、使用引用的时机:重载流操作符的参数、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、以及其它部分情况下都推荐使用引用。,6.4使用引用,202

38、3/3/31,74,6.4.2 独立引用 1、初始化时,赋值表达式的右端是一个变量。例如:int,6.4使用引用,2023/3/31,75,例6_11:引用变量和被引用变量的值同步变化.smain6_11.cpp,500600600500600600,6.4使用引用,2023/3/31,76,6.4.3 引用作为函数参数 值传递只能实现实参到形参的传值,不能实现形参到实参的反向传值。C+中,需要双向传值时,还可以采用引用。使用引用比使用指针传值更安全,更方便,更直观,更容易理解。例6_12:引用作为参数,产生了双向传值的效果。smain6_12.cpp,6.4使用引用,2023/3/31,77

39、,提高传值的效率:采用引用传值,可以避免复制传值过程,相应地提高了传值的效率。引用就是实参对象本身,不需要复制。如果采用常引用,也不会改变实参,对实参来说也是安全的。例6_13:常引用作参数,在不需要修改实参的时候,保证了所传递参数的安全性。而且传递效率高。smain6_13.cpp,6.4使用引用,2023/3/31,78,6.4.4 引用返回值 函数返回引用,返回的是一个存储单元(即变量)。因此,如果一个函数返回引用的话,则函数调用可以出现在赋值号的右边。也可以出现在赋值号的左边。例6_14:引用作为返回值。smain6_14.cpp,6.4使用引用,2023/3/31,79,函数返回引用

40、作为左值使用的结果。在函数中未对数组元素作任何修改,但是数组元素值却改变了。这是函数返回引用,并被作为左值操作的结果。,100 10200 102,4,6,8,10,12,14,16,18,20,2,4,6,8,10,12,100,16,18,20,6.4使用引用,2023/3/31,80,6.4.5 常引用常引用是用const修饰的引用,它常常用做形式参数,用来限制对实参对象的修改。例如:/参数是常引用,在函数中不可改变它。void Func(const CStudent,6.4使用引用,2023/3/31,81,例6_15:从源复制指定大小的字节数到目的。smain6_15.cpp 程序中

41、,使用了“const void*pvFrom”作为形式参数,这保证了实际参数lpszSource的安全性。同时也不会采用复制传值,直接使用lpszSource,传递效率最高。,6.4使用引用,2023/3/31,82,6.5 类型转换,类型转换就是将一种类型转换为另一种类型:标准类型之间可以相互转换。类类型之间,标准类型和类类型之间也可以相互转换。对于标准类型来说,C+提供了隐式类型转换和显示类型转换两种机制。而自定义类类型到基本类型的转换则可由自己定义的构造函数和类型转换函数来实现。,2023/3/31,83,6.5.1 构造函数和类型转换函数 利用构造函数只能完成基本类型到类类型的转换。从

42、类类型到基本类型的转换需要定义类型转换函数。1 基本类型转化为类类型用构造函数进行类型转换有一个前提,就是类中有一个只带一个参数的构造函数。例6_16:实现由实型数,字符串到CDouble类型的转换。smain6_16.cpp,6.5 类型转换,2023/3/31,84,构造函数(1)完成从 double 型到CDouble类类型的转换;构造函数(2)完成从char*到CDouble类类型的转换,虽然它有2个参数,但第2个参数缺省,所以它也可以只带一个参数转换。,6.5 类型转换,2023/3/31,85,6.5.1.2 类类型转化为基本类型完成类类型到基本类型的转换,可采用类类型转换函数来实

43、现。类类型转换函数专门用来将类类型转换为基本数据类型。operator()/return;,无返回类型,6.5 类型转换,2023/3/31,86,没有返回类型,就代表了它的返回类型;没有任何参数。在调用过程中要带一个对象实参。类类型转换函数没有返回类型说明,但函数体必须有return语句,用于返回。例6_17的operator int();定义了一个类类型转换为基本类型的例子。smain6_17.cpp,6.5 类型转换,2023/3/31,87,CInteger:operator int()return m_iNum;定义类类型转换函数时,应注意下列几点:1、转换函数是类的非静态成员函数。

44、2、转换函数定义时,不进行返回值的类型说明,也没有形参。,6.5 类型转换,2023/3/31,88,6.5.2 一个类型转换实例 例6_18:直角坐标到极(矢量)坐标的相互转换。smain6_18.cpp,6.5 类型转换,X坐标为:2;Y坐标为:2X坐标为:0;Y坐标为:0极径=4;极角=0.523333弧度。极径=0;极角=0弧度。X坐标为:2;Y坐标为:2X坐标为:3.46463;Y坐标为:1.99908极径=4;极角=0.523333弧度。极径=2.82843;极角=0.785398弧度。,2023/3/31,89,6.6 异常处理,出错处理即异常处理是提高程序健壮性的重要手段。异常

45、主要指程序运行时异常。异常处理机制就是用于管理这种异常的一种方法。异常处理的基本结构throw、try和catch语句的一般语法如下:,2023/3/31,90,throw;try/try语句块 catch(类型1 参数1)/针对类型1的异常处理,catch(类型2 参数2)/针对类型2的异常处理/catch(类型n 参数n)/针对类型n的异常处理,1个参数,6.6 异常处理,2023/3/31,91,在设计catch程序时,catch出现的顺序很重要。在一个try块中引发异常时,异常处理程序按照它在catch中出现的顺序进行检查。catch的排列顺序应从特殊到一般的排列顺序(指异常对象从特殊

46、到一般排列)。即应该将派生的异常捕获放在前面,而将基类异常对象的捕获放在处理程序后面。,6.6 异常处理,2023/3/31,92,6.6.1 C语言的出错处理C语言的出错处理方法,无法正确地清除对象。C+语言异常处理将异常的检测与处理分开。C+异常处理机制采用结构化方法,就是:throw、try、catch结构。1、throw:用来创建用户自定义类型的异常错误,用于抛出异常。2、try:标识程序中异常语句块,用于引发异常。3、catch:标识异常错误处理模块,用于捕获、处理异常。,6.6 异常处理,2023/3/31,93,6.6.2 抛出异常 throw;使用一般方式if(条件)throw

47、 异常规格申明(下个页面还将详细介绍)比如:void ExceptionFunction(argument)throw(ExceptionClass1,ExceptionClass2)表示函数ExceptionFunction中可抛出ExceptionClass1或ExceptionClass2类型的异常.如果某段程序发现了自己不能处理的异常,就可以使用throw表达式抛出这个异常,将它抛给调用者。,6.6 异常处理,2023/3/31,94,异常规格申明,异常规格申明是C+函数申明的一部分,它们指定了函数可以抛出什么异常 例如void f1()throw(int)可以抛出一个整型异常void

48、 f2()throw(char*,E)可以抛出一个char*或一个E(这里E是用户自定义类型)类型的异常。void f3()throw()表明函数不抛出异常void f4()表明函数可以抛出任何东西。,2023/3/31,95,6.6.3 捕获异常由catch语句完成。要捕获异常,必须把可能产生异常的代码封装在try块中。try块的语法格式如下:try,6.6 异常处理,2023/3/31,96,6.6.4 处理异常异常抛出时,程序就从正常程序流转入异常处理器中。为了恢复程序的正确执行,需要保证异常抛出时对象被正确地清除。C+的异常处理器可以保证离开作用域时,作用域中的所有结构完整的对象的析构

49、函数都被调用,以清除这些对象。这个工作也由紧跟在try块后的catch来执行,可以有一个或多个catch块,每个catch块指定捕获和处理一种异常。,6.6 异常处理,2023/3/31,97,6.6.5 异常的匹配异常匹配不要求在异常和处理器之间匹配得十分完美。一个对象或一个派生类对象的引用可以与基类处理器匹配。所以,异常处理catch语句的排列顺序应该将基类匹配放在最后一个catch块中。,6.6 异常处理,2023/3/31,98,6.6.6 标准异常标准C+库中的异常类包含9个异常类,它们可以分为运行时异常和逻辑异常两种。6.6.7 异常开销 异常的发生并不多,所以开销聚集在异常发生时

50、,而不会表现在普通的执行代码上。6.6.7 含有异常的典型程序设计例6_19:显示了一个典型的异常处理程序的代码。注意从输出结果观察异常的执行顺序。smain6_19.cpp,6.6 异常处理,2023/3/31,99,In main.In try block,calling MyFunc().Constructing CDoctorDemo.In MyFunc().Throwing CMyException exception.Destructing CDoctorDemo.In catch handler.Caught CMyException exception type:Excepti

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

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


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号