1、2022-12-51动态内存管理技术动态内存管理技术C C+常见问题常见问题Const的使用的使用内联函数的使用内联函数的使用使用引用使用引用类型转换类型转换异常处理异常处理命名空间命名空间内容提要内容提要2022-12-526.1内联函数的使用内联函数的使用6.1.1 内联函数引入的原因内联函数引入的原因 p调用函数时,要跳到函数的起始地址去执行,调用函数时,要跳到函数的起始地址去执行,执行完函数的代码后,再返回到调用点继续执执行完函数的代码后,再返回到调用点继续执行。行。p这种跳转操作需要这种跳转操作需要保存现场保存现场及返回地址;返回及返回地址;返回时,又需要时,又需要恢复现场恢复现场。
2、p函数调用需要一定的函数调用需要一定的时间开销时间开销和和空间开销,空间开销,这这会影响程序的执行效率。会影响程序的执行效率。外联开销外联开销2022-12-53p对内联函数,编译器会在程序出现内联函数调对内联函数,编译器会在程序出现内联函数调用的地方用的地方用函数体直接进行替换用函数体直接进行替换。p目标程序将目标程序将不存在调用不存在调用问题,也就问题,也就不会产生跳不会产生跳转转的问题,自然就的问题,自然就不存在函数调用所需要的时不存在函数调用所需要的时间和空间开销。间和空间开销。p在编译时用函数代码替换到调用处,如果函数在编译时用函数代码替换到调用处,如果函数代码长度比较长,超过了跳转
3、语句所占空间的代码长度比较长,超过了跳转语句所占空间的长度,这自然会增加整个程序的代码量,进而长度,这自然会增加整个程序的代码量,进而增加了空间的开销增加了空间的开销。实质是用空间换时间。实质是用空间换时间。6.1内联函数的使用内联函数的使用2022-12-546.1.2 内联函数的定义内联函数的定义 例例6_1:smain6_1.cpp/1、全局函数定义为内联函数、全局函数定义为内联函数inline double Show1(double dX)return dX*dX;6.1内联函数的使用内联函数的使用2022-12-55/2、在、在类声明体内实现类声明体内实现的函数不用的函数不用inli
4、ne关键字,关键字,自动是内联函数。自动是内联函数。double Show2(double dX)constreturn dX*dX;6.1内联函数的使用内联函数的使用2022-12-56/3、在函数声明处可加可不加、在函数声明处可加可不加inline关键字。关键字。double Show3(double dX)const;/4、但在实现处必须加、但在实现处必须加inline关键字关键字inline double CPerson:Show3(double dX)constreturn dX*dX;6.1内联函数的使用内联函数的使用2022-12-57p内联函数使用时注意以下几点。内联函数使用时
5、注意以下几点。1、在内联函数中、在内联函数中不允许用循环语句、开关语句不允许用循环语句、开关语句和递归调用语句和递归调用语句等。等。2、内联函数的定义必须出现在第一次调用内联、内联函数的定义必须出现在第一次调用内联函数之前。函数之前。3、在类内部定义并实现的成员函数、在类内部定义并实现的成员函数自动自动是内联是内联函数,但在类内定义成员函数时,如果使用了函数,但在类内定义成员函数时,如果使用了for()、while()、do while()、switch()等等语句,该成员函数会语句,该成员函数会自动转为非内联函数自动转为非内联函数。4、内联函数中、内联函数中不能够有静态数据不能够有静态数据。
6、5、内联函数中、内联函数中不能够有数组说明不能够有数组说明。6.1内联函数的使用内联函数的使用2022-12-586.1.3 内联和非内联函数的选择使用内联和非内联函数的选择使用p内联函数时间开销的减少,内联函数时间开销的减少,以空间的消耗为代以空间的消耗为代价价,而且,而且执行效率的提高也不是绝对的执行效率的提高也不是绝对的。p在决定使用内联函数时要进行适当的取舍。在决定使用内联函数时要进行适当的取舍。p不是所有的函数都适合采用内联函数不是所有的函数都适合采用内联函数。p构造函数和析构函数常常就不适合内联构造函数和析构函数常常就不适合内联,这是,这是因为构造函数和析构函数在编译时,往往会被因
7、为构造函数和析构函数在编译时,往往会被编译器附加一大堆代码。编译器附加一大堆代码。全部内联可以吗?全部内联可以吗?6.1内联函数的使用内联函数的使用2022-12-59p看看看看CStudent类的构造函数:类的构造函数:p例例6_2:构造函数、析构函数往往不适合作构造函数、析构函数往往不适合作内联内联函数。函数。psmain6_2.cpp 构造函数、析构函数最好不要内联。构造函数、析构函数最好不要内联。6.1内联函数的使用内联函数的使用2022-12-510pCStudent()构造函数看起来是构造函数看起来是空的空的,但它可,但它可能含有相当多的代码(与编译器的设计有关)。能含有相当多的代
8、码(与编译器的设计有关)。pCStudent的构造函数,需要构建的构造函数,需要构建4000字节的字节的空间,并且还需要按照基类的要求初始化这些空间,并且还需要按照基类的要求初始化这些空间,因此它包含了一个复杂的创建过程空间,因此它包含了一个复杂的创建过程。p构造函数和析构函数最好都在构造函数和析构函数最好都在类内声明,类外类内声明,类外实现实现。p一般来说,实际编程时一般来说,实际编程时最好最好是不要内联任何函是不要内联任何函数,除非函数确实很小很简单。数,除非函数确实很小很简单。不要内联任何函数不要内联任何函数6.1内联函数的使用内联函数的使用2022-12-5116.2 const的使用
9、的使用pconst意味着分配了一块意味着分配了一块不可改变的内存不可改变的内存。p用用const定义或说明常类型量时定义或说明常类型量时必须初始化必须初始化。p常类型量,也就是常量。常类型量,也就是常量。pconst更重要的用途则在于更重要的用途则在于说明函数参数以及说明函数参数以及函数返回类型函数返回类型。函数参数函数参数2022-12-512pconst主要有以下几种用法:主要有以下几种用法:1、const说明值常量。说明值常量。2、const说明指针。说明指针。3、const说明函数参数及其返回值。说明函数参数及其返回值。4、const说明类中的常量和成员函数。说明类中的常量和成员函数。
10、6.2 const的使用的使用2022-12-5136.2.1 const说明值常量说明值常量 1、说明、说明符号常量符号常量,表明符号代表的是一个常量,表明符号代表的是一个常量,说明格式如下:说明格式如下:pdouble const PI=12;pconst double PI=12;2、说明、说明数组常量数组常量,说明格式如下:,说明格式如下:pconst int I_ARRAY=3,2,1;3、说明、说明对象常量对象常量,说明格式如下:,说明格式如下:pconst CInline1 oC1,oC2;p在一排逗号隔开的变量中,在一排逗号隔开的变量中,只需要一个只需要一个const就行了就行
11、了在前、在后定义都可以。在前、在后定义都可以。6.2 const的使用的使用2022-12-514const与与C语言语言#define的差别的差别:p#define PI 3.14p无参宏不是符号常量无参宏不是符号常量,它没有数据类型,没有,它没有数据类型,没有值,在内存中不分配地址。它是在预处理时作值,在内存中不分配地址。它是在预处理时作宏替换,不作类型检查。宏替换,不作类型检查。p而而const定义的常量是符号常量定义的常量是符号常量,有数据类型,有数据类型,有值,且其值不可改变,在内存中有地址,编有值,且其值不可改变,在内存中有地址,编译时要作类型检查。译时要作类型检查。6.2 con
12、st的使用的使用2022-12-5156.2.2 const与指针与指针 指针涉及三个东西:指针涉及三个东西:p一个是一个是指针变量本身指针变量本身;p另一个是另一个是指针指针指针变量中所存放的值;指针变量中所存放的值;p再一个就是再一个就是指针所指向的对象指针所指向的对象。6.2 const的使用的使用2022-12-516p指针其实就是地址指针其实就是地址,它代表的是指针变量中存,它代表的是指针变量中存放的地址值;放的地址值;p而而指针变量是指用来存放指针的一个变量指针变量是指用来存放指针的一个变量,它,它用来存放地址;用来存放地址;p注意注意:指针变量本身也有地址,有存储单元;:指针变量
13、本身也有地址,有存储单元;p指针所指向的对象是指那片空间所指针所指向的对象是指那片空间所“存放的东存放的东西西”。p指针变量本身的地址,与指针变量所指向的对指针变量本身的地址,与指针变量所指向的对象的存储地址是两个不同的地址。象的存储地址是两个不同的地址。6.2 const的使用的使用2022-12-5176.2 const的使用的使用2022-12-518pconst和指针变量的典型组合有以下三种情况:和指针变量的典型组合有以下三种情况:1、指向常量的指针、指向常量的指针 const int*p;2、常指针、常指针 char*const pc=a;3、指向常量的常指针、指向常量的常指针con
14、st char*const pc=a;6.2 const的使用的使用2022-12-519 const int *p;char *const pc=a;const char *const pc=a;p画一条垂直线穿过指针声明中的星号(画一条垂直线穿过指针声明中的星号(*)位)位置。置。p如果如果const出现在线的左边,则指针指向的对出现在线的左边,则指针指向的对象为常量;象为常量;p如果如果const出现在线的右边,则指针本身为常出现在线的右边,则指针本身为常量;量;p如果如果const在线的两边都出现,则二者都是常在线的两边都出现,则二者都是常量,是指向常量的常指针。量,是指向常量的常指针
15、。6.2 const的使用的使用 2022-12-5201 指向常量的指针指向常量的指针 p指针指向一个指针指向一个不可改变的量。不可改变的量。指向常量。指向常量。p在声明时在声明时可以不初始化。可以不初始化。p该指针可以指向这个常量,也可以指向另一个该指针可以指向这个常量,也可以指向另一个常量。常量。p指针变量里的指针值是可以改变的;指针变量里的指针值是可以改变的;p但某一个具体指针值(即:某个指针)但某一个具体指针值(即:某个指针)所指向所指向的那个对象则是不可以改变的的那个对象则是不可以改变的。对象不可改变。对象不可改变。6.2 const的使用的使用2022-12-521p它也可以指向
16、变量;它也可以指向变量;p从指针角度而言,所指向的是一个常量,通过从指针角度而言,所指向的是一个常量,通过该指针该指针不能修改不能修改它所指向的对象;它所指向的对象;p该指针变量可以存放常量地址也可以存放变量该指针变量可以存放常量地址也可以存放变量地址。地址。对象不可改变。对象不可改变。6.2 const的使用的使用2022-12-522const char*pc=ABCD;pc3=a;/不可以。不可以。pc=“EFGH”;/可以可以/step是一个指向常量的指针数组是一个指向常量的指针数组 const char*step3=left,right,top;step2=skip;/可以。可以。s
17、tep21=i;/不可以。不可以。6.2 const的使用的使用2022-12-5232 常指针常指针 *const=;p指针变量里面存放的是个常量,称为指针变量里面存放的是个常量,称为常指针常指针,指针变量里面装的是一个固定值,指针变量里面装的是一个固定值,在定义时必在定义时必须初始化。须初始化。p常指针一旦初始化后,不能够再指向其他内存常指针一旦初始化后,不能够再指向其他内存单元。单元。p通过常指针通过常指针可以修改它所指向的内存单元的内可以修改它所指向的内存单元的内容容,也就是修改常指针所指向的对象。,也就是修改常指针所指向的对象。6.2 const的使用的使用2022-12-524ch
18、ar*const pc=ABCD;/常指针常指针pc3=a;/可以。可以。pc=EFGH;/不可以不可以/step是一个常指针数组是一个常指针数组 char*const step3=left,right,top;step2=skip;/不可以。不可以。step21=i;/可以。可以。6.2 const的使用的使用2022-12-5253 指向常量的常指针指向常量的常指针 p指针本身及指针所指向的对象都不可改变。二指针本身及指针所指向的对象都不可改变。二者都要声明为者都要声明为 const。p在声明时必须初始化。在声明时必须初始化。p指针本身及其指向的对象均不能改变。指针本身及其指向的对象均不能
19、改变。pconst char*const pc=asdf;ppc3=a;/不可以。不可以。ppc=ABCD;/不可以。不可以。6.2 const的使用的使用2022-12-5266.2.3 const说明函数参数和返回值说明函数参数和返回值p是是const最重要的应用。最重要的应用。p在在值传递场合值传递场合不必用不必用const。p用用const去修饰用去修饰用指针和引用传递的函数参数指针和引用传递的函数参数,是安全的。是安全的。pconst修饰函数的返回值。修饰函数的返回值。安全的安全的6.2 const的使用的使用2022-12-527void Func(const char*lpszC
20、har);void Func(char const*lpszChar);p/以上二者等价。以上二者等价。p/参数是对象的常引用,返回值也是一个对象参数是对象的常引用,返回值也是一个对象的常引用。的常引用。pconst CStudent&Func(const CStudent&oCStudent);p对返回值使用对返回值使用const也有可能也有可能提高函数的安全提高函数的安全性和效率。性和效率。6.2 const的使用的使用2022-12-5286.2.4 类中的类中的constp在类中,在类中,const的使用也比较广。的使用也比较广。p它可以用于修饰类中的它可以用于修饰类中的成员函数和成员
21、数据成员函数和成员数据。主要应用主要应用6.2 const的使用的使用2022-12-5291 常成员函数常成员函数 p不可以改变对象数据成员的函数。不可以改变对象数据成员的函数。在一般成员在一般成员函数后面加上函数后面加上const即可即可。1、用、用const关键词修饰一个成员函数,说明该关键词修饰一个成员函数,说明该成员函数成员函数是是“只读只读”的的。2、只有常成员函数可以操作常对象,、只有常成员函数可以操作常对象,常对象只常对象只能调用常成员函数能调用常成员函数(const成员函数)。成员函数)。3、一般对象不但可以调用一般成员函数,也可、一般对象不但可以调用一般成员函数,也可以调用
22、常成员函数。以调用常成员函数。6.2 const的使用的使用2022-12-530p例例6_3:常对象只能够调用常成员函数,不能够常对象只能够调用常成员函数,不能够调用一般成员函数,而一般对象可以调用所有调用一般成员函数,而一般对象可以调用所有的成员函数。的成员函数。s6_3sclass6_3_T.h s6_3sclass6_3_point.cpp s6_3smain6_3.cpp运行结果运行结果 一般成员函数调用:一般成员函数调用:1 2一般成员函数调用:一般成员函数调用:2 3 常成员函数调用:常成员函数调用:3 3 6.2 const的使用的使用2022-12-531p例子表明:例子表明
23、:仅在仅在const方面有不同的成员函数方面有不同的成员函数是可以重载的,是可以重载的,编译器能够正确区分这两个函编译器能够正确区分这两个函数。数。p而对调用对象来说,一般对象而对调用对象来说,一般对象优先调用重载的优先调用重载的一般函数,其次才是一般函数,其次才是const函数函数;p常对象(常对象(const对象)则只能够调用对象)则只能够调用const函函数,不能够调用一般函数。数,不能够调用一般函数。p对于一般对象对于一般对象,当调用一般成员函数不成功时,当调用一般成员函数不成功时,系统自动会转入对常成员函数的调用。系统自动会转入对常成员函数的调用。6.2 const的使用的使用202
24、2-12-5322 常数据成员常数据成员p若一个数据成员的前面用了关键词若一个数据成员的前面用了关键词const修饰修饰符,则该数据成员为常数据成员。符,则该数据成员为常数据成员。pconst 型的数据型的数据必须初始化,必须初始化,且不能修改,它且不能修改,它是常数据成员。是常数据成员。p常数据成员的初始化方式常数据成员的初始化方式在定义对象时,在定义对象时,通过在构造函数后面加上成员通过在构造函数后面加上成员初始化列表初始化列表来完来完成的。成的。psmain6_4.cpp 6.2 const的使用的使用2022-12-533class CPoint/CPoint类类private:int
25、 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的使用的使用2022-12-534p在在CPoint中设置了空间坐标中的中设置了空间坐标中的m_x、m_y和
26、和m_z坐标。坐标。p分别把它们设置成一般数据成员、常数据成员分别把它们设置成一般数据成员、常数据成员和静态常数据成员。由于类型不同,它们所采和静态常数据成员。由于类型不同,它们所采用的初始化方式是很不一样的。用的初始化方式是很不一样的。p在设计构造函数时,最好在设计构造函数时,最好对一般成员数据也采对一般成员数据也采用初始化表的方式进行初始化用初始化表的方式进行初始化。p这样效率最高。这样效率最高。6.2 const的使用的使用2022-12-5353 常对象常对象p常对象就是其值常对象就是其值不可改变不可改变的对象的对象。它是只可。它是只可以取值不可以修改的对象。以取值不可以修改的对象。p
27、常对象一旦建立并初始化以后,只可以通过常常对象一旦建立并初始化以后,只可以通过常成员函数来访问它。成员函数来访问它。pconst CPoint ocCPoint3(3,3);6.2 const的使用的使用2022-12-5366.3 动态内存管理技术动态内存管理技术 p动态对象就是程序在运行过程中在动态内存中动态对象就是程序在运行过程中在动态内存中建立的对象。建立的对象。p这类对象需要用户自己使用这类对象需要用户自己使用new运算符创建,运算符创建,使用使用delete运算符释放,运算符释放,需要用户自己管理需要用户自己管理。堆栈静态内存静态内存2022-12-5376.3.1 内存的几种分配
28、方式内存的几种分配方式p计算机内存通常具有三种组织方式:计算机内存通常具有三种组织方式:堆、栈和堆、栈和静态内存静态内存。1、在静态存储区中分配。在静态存储区中分配。p静态存储区中的静态存储区中的变量空间变量空间在编译时分配。在编译时分配。p静态内存区中的静态内存区中的变量变量在程序的整个运行期间都在程序的整个运行期间都存在。存在。p其生命周期贯穿整个程序的运行周期。其生命周期贯穿整个程序的运行周期。p例如全局变量,例如全局变量,static变量等在这个区间分配。变量等在这个区间分配。6.3 动态内存管理技术动态内存管理技术 2022-12-5382、在栈上分配、在栈上分配p自动变量等局部变量
29、在栈上分配存储空间。自动变量等局部变量在栈上分配存储空间。p函数内的局部变量函数内的局部变量在栈上分配存储单元在栈上分配存储单元。p它的生命周期与函数的执行时间相同。它的生命周期与函数的执行时间相同。p当函数执行结束时这些存储单元会被自动释放,当函数执行结束时这些存储单元会被自动释放,其生命周期也就完结了。其生命周期也就完结了。6.3 动态内存管理技术动态内存管理技术 2022-12-5393、在堆上分配,亦称动态内存分配。、在堆上分配,亦称动态内存分配。p动态内存由程序员自己负责管理。动态内存由程序员自己负责管理。p运行时用运行时用new申请内存。申请内存。p程序员负责在不需要时用程序员负责
30、在不需要时用delete释放内存。释放内存。比拉登还麻烦。比拉登还麻烦。容易出问题。容易出问题。6.3 动态内存管理技术动态内存管理技术 2022-12-5406.3.2 使用使用new和和delete分配和释放动态内存分配和释放动态内存pmalloc()与)与free()标准库函数;标准库函数;pnew/delete运算符运算符(不是库函数)。(不是库函数)。pmalloc()()/free()功能有限。()功能有限。p用户自定义类型在创建对象时要执行构造函数,用户自定义类型在创建对象时要执行构造函数,对象在消亡之前要执行析构函数。对象在消亡之前要执行析构函数。p而且是而且是自动执行自动执行
31、的。的。p所以,所以,C+使用能完成动态内存分配和初始化使用能完成动态内存分配和初始化工作的运算符工作的运算符new,及能完成清理与释放内存,及能完成清理与释放内存工作的运算符工作的运算符delete来管理动态内存。来管理动态内存。6.3 动态内存管理技术动态内存管理技术 2022-12-5416.3.2.1 new运算符运算符 pnew用来用来动态地分配存储空间。动态地分配存储空间。p它能够它能够自动计算自动计算要分配的存储空间大小并能返要分配的存储空间大小并能返回正确的指针类型。回正确的指针类型。p若返回值为若返回值为NULL,则表示动态内存分配不成,则表示动态内存分配不成功。功。p在用在
32、用new申请动态内存后,申请动态内存后,一定要判断一定要判断动态内动态内存是否分配成功。存是否分配成功。if(p!=NULL)6.3 动态内存管理技术动态内存管理技术 2022-12-542int*pInt1,*pInt2;pInt1=new int;/new 运算返回一整数单元的地址运算返回一整数单元的地址pInt2=new int(200);/new一个一个int单元,并将其初单元,并将其初始化为始化为200。int*pInt3,(*pInt4)3;pInt3=new int10;/new 运算返回一整数单元的地址,运算返回一整数单元的地址,有有10个这样的单元。个这样的单元。pInt4=
33、new int23;/new 运算返回每行为运算返回每行为3个元个元素的行地址素的行地址CPoint*poCPoint;poCPoint=new CPoint(10,10);/new 运算返回运算返回对象的地址,并在动态内存中创建了一个对象。对象的地址,并在动态内存中创建了一个对象。6.3 动态内存管理技术动态内存管理技术 2022-12-543p使用使用new运算符创建数组时,运算符创建数组时,不能为该数组指不能为该数组指定初值定初值。p执行执行“CPoint poCPoint=new CPoint(10,10);”语句时,得到了两个东西:语句时,得到了两个东西:p一个是对象指针变量,一个是
34、对象指针变量,p另一个是在动态内存空间中的对象,另一个是在动态内存空间中的对象,p而指针变量中的值,就是这个动态内存空间中而指针变量中的值,就是这个动态内存空间中的对象的首地址,也就是动态内存地址。的对象的首地址,也就是动态内存地址。p而且这个对象是而且这个对象是没有名字没有名字的,只有通过指针进的,只有通过指针进行访问。行访问。6.3 动态内存管理技术动态内存管理技术 2022-12-5442 delete运算符运算符pdelete释放释放new所分配的存储空间。所分配的存储空间。p是存放了要释放的存储空间地址的是存放了要释放的存储空间地址的指针变量名字。指针变量名字。p释放释放new所创建
35、数组时,使用所创建数组时,使用“delete ;”的形式。的形式。pdelete只能用来释放用只能用来释放用new申请得的动态内存申请得的动态内存空间,而且空间,而且一次一次new只能够对应一次只能够对应一次delete。配对使用配对使用6.3 动态内存管理技术动态内存管理技术 2022-12-545delete pInt1;delete pInt2;delete pInt3;/释放用释放用new申请的数组空间申请的数组空间p/释放由释放由new申请的数组空间,对多维空间的申请的数组空间,对多维空间的释放格式与一维空间相同。释放格式与一维空间相同。delete pInt4;delete poC
36、Point;/释放一个对象释放一个对象6.3 动态内存管理技术动态内存管理技术 2022-12-546注意:注意:pdelete后,指针所指向的内存空间就释放了,后,指针所指向的内存空间就释放了,但但指针变量的值并没有改变指针变量的值并没有改变。p指针还指向原来的内存空间。指针还指向原来的内存空间。p所以,所以,delete以后,应将指针变量的值以后,应将指针变量的值设置成设置成NULL,让它不再指向原来的内存空间,以免,让它不再指向原来的内存空间,以免误操作。误操作。例例6_5:显示了显示了new和和delete的基本使用的基本使用 smain6_5.cpp 6.3 动态内存管理技术动态内存
37、管理技术 2022-12-547CPoint*poCPoint;/创建对象时,自动调用构造函数。创建对象时,自动调用构造函数。poCPoint=new CPointARRAY_SIZE;/判断动态内存申请是否成功。判断动态内存申请是否成功。if(poCPoint=NULL)cout 动态内存分配失败。动态内存分配失败。n;exit(0);/内存申请失败内存申请失败,则退出则退出.6.3 动态内存管理技术动态内存管理技术 2022-12-548delete poCPoint;/释放对象数组。释放对象数组。/动态内存释放后,让动态内存释放后,让poCPoint指针指向指针指向NULL。否则,它依然
38、指向原动态空间。否则,它依然指向原动态空间。poCPoint=NULL;其内容为垃圾其内容为垃圾6.3 动态内存管理技术动态内存管理技术 2022-12-549p在例在例6_5中,数组中的中,数组中的5个对象其坐标都是(个对象其坐标都是(0,0,0)。)。p这是由于这是由于给数组分配空间时,不能进行初始化给数组分配空间时,不能进行初始化(声明时不能够带参数)。(声明时不能够带参数)。p也就是说:也就是说:new一个对象数组时是不能够带参一个对象数组时是不能够带参数的。数的。p缺省的参数则可以缺省的参数则可以,构造函数缺省值为(,构造函数缺省值为(0,0,0)。)。6.3 动态内存管理技术动态内
39、存管理技术 2022-12-550p对象数组的对象对象数组的对象初始化有两种方法初始化有两种方法:1、在类中不定义构造函数,而定义一个、在类中不定义构造函数,而定义一个成员函成员函数数专门用来完成初始化。专门用来完成初始化。2、在所定义的类中增加不带参数或带缺省参数、在所定义的类中增加不带参数或带缺省参数的的构造函数构造函数。6.3 动态内存管理技术动态内存管理技术 2022-12-551ppoCPoint=new CPointARRAY_SIZE;“表明对应着:表明对应着:delete poCPoint;;p而:而:CTemp*poCTemp=new CTemp(5);”对应着:对应着:de
40、lete poCTemp;。p如果如果new和和delete形式不对应,结果将不可预形式不对应,结果将不可预测。测。6.3 动态内存管理技术动态内存管理技术 2022-12-5523 new和和delete的重载的重载 pnew和和delete可以在类内重载,也可以在类外可以在类内重载,也可以在类外重载。重载。p类外重载就是类外重载就是全局函数重载全局函数重载,它会覆盖系统预,它会覆盖系统预定义的定义的new和和delete功能,。功能,。p类内:类内:new和和delete只能重载为成员函数只能重载为成员函数,不,不能重载为友元函数。而且,无论是否使用关键能重载为友元函数。而且,无论是否使用
41、关键字字static进行修饰,重载了的进行修饰,重载了的new和和delete均均为类的为类的静态成员函数静态成员函数。6.3 动态内存管理技术动态内存管理技术 2022-12-553void*operator new(size_t size)void*p=malloc(size);return(p);void operator delete(void*p);free(p);6.3 动态内存管理技术动态内存管理技术 2022-12-5546.3.3 常见的动态内存错误常见的动态内存错误p编译器不能自动发现动态内存错误。在程序运行时才编译器不能自动发现动态内存错误。在程序运行时才能捕捉到。常见的
42、动态内存错误有:能捕捉到。常见的动态内存错误有:1、使用未分配成功的动态内存空间。、使用未分配成功的动态内存空间。p在使用动态内存之前必须检查指针是否为在使用动态内存之前必须检查指针是否为NULL:p函数入口处函数入口处:assert(p!=NULL)assert()函数对应的头文件为函数对应的头文件为:assert.h 参考参考C语言程序设计(语言程序设计(C99),),清华大学出版社清华大学出版社p申请时用申请时用:if(p=NULL)或或if(p!=NULL)进行检查进行检查.6.3 动态内存管理技术动态内存管理技术 2022-12-5552、使用未经初始化的动态内存空间。、使用未经初始
43、化的动态内存空间。p内存的缺省初值可能是一个内存的缺省初值可能是一个不确定的值不确定的值,所以,所以,任何内存空间的使用都应该立足于自己初始化。任何内存空间的使用都应该立足于自己初始化。尽量尽量不要使用系统缺省的初值不要使用系统缺省的初值。3、超过了内存空间的边界使用、超过了内存空间的边界使用 p指针指向了不正确的类型时,会出错;指针指向了不正确的类型时,会出错;p数组的操作也很容易越界;数组的操作也很容易越界;p越界后就会操作到不正确的内存单元越界后就会操作到不正确的内存单元.6.3 动态内存管理技术动态内存管理技术 2022-12-5564、内存泄露。、内存泄露。p是一种是一种严重的内存错
44、误严重的内存错误,极不容易发现。,极不容易发现。p程序长期运行的话,最终会导致内存耗尽。程序长期运行的话,最终会导致内存耗尽。p由于由于new和和delete使用不配对使用不配对new创建的动态创建的动态空间,没有用空间,没有用delete释放等。释放等。5、使用已经释放了的内存空间。、使用已经释放了的内存空间。p用用free或或delete释放内存以后,指针变量释放内存以后,指针变量p的的值并没有改变,它依然指向原来值并没有改变,它依然指向原来new的那一片的那一片内存空间。释放内存以后,内存空间。释放内存以后,一定要将指针变量一定要将指针变量的值置成的值置成NULL。另外,在传递内存指针时
45、要另外,在传递内存指针时要避免传递栈内存避免传递栈内存(存储临时变量的内存)(存储临时变量的内存)。6.3 动态内存管理技术动态内存管理技术 2022-12-5576.3.4 指针和数组的对比指针和数组的对比p数组名对应着一块内存,其地址与容量在生命数组名对应着一块内存,其地址与容量在生命期内保持不变,期内保持不变,数组的内容则可以改变;数组的内容则可以改变;p指针变量指针变量(尤其尤其void*)则是一个变量,它可以存则是一个变量,它可以存放任意的地址值,它可以随时指向任意类型的放任意的地址值,它可以随时指向任意类型的内存块,所以指针远比数组灵活、危险。内存块,所以指针远比数组灵活、危险。例
46、例6_7说明了指针和数组在使用上的一些差异。说明了指针和数组在使用上的一些差异。使用不慎将引发内存问题。使用不慎将引发内存问题。smain6_7.cpp 6.3 动态内存管理技术动态内存管理技术 p Oellop worldp hellop hellop 12p 42022-12-5586.3.5 利用指针传递内存的方式利用指针传递内存的方式 p利用函数申请动态内存,可以使用指向指针的利用函数申请动态内存,可以使用指向指针的指针、指针引用以及函数返回指针来传递动态指针、指针引用以及函数返回指针来传递动态内存。内存。例例6_8:利用指向指针的指针、指针引用和函数返利用指向指针的指针、指针引用和函
47、数返回指针传递内存。回指针传递内存。smain6_8.cpp hello,world.hello,china.hello,四川大学计算机学院!四川大学计算机学院!6.3 动态内存管理技术动态内存管理技术 2022-12-559例例6_9:利用指针传递内存时,注意不要传递栈内利用指针传递内存时,注意不要传递栈内存,否则指针会指向垃圾。存,否则指针会指向垃圾。smain6_9.cpp p 栈内存调用的结果为:栈内存调用的结果为:,p静态内存调用的结果为:静态内存调用的结果为:hello worldp在例在例6_9中:中:pGetString1的的return语句返回了指向语句返回了指向“栈内存栈内
48、存”的指针,它是错误的,因为该内存在函数结束的指针,它是错误的,因为该内存在函数结束时自动消失;而时自动消失;而GetString2虽然返回了正确的虽然返回了正确的结果,但它在设计概念上就已经错误了结果,但它在设计概念上就已经错误了(因得因得到的是常量空间到的是常量空间)。6.3 动态内存管理技术动态内存管理技术 2022-12-560p以下两者本质上是不同的以下两者本质上是不同的:pchar aChar =hello world;pchar*aChar=hello world;动态数组初始化动态数组初始化6.3 动态内存管理技术动态内存管理技术 2022-12-5616.3.6 delete
49、的作用的作用pdelete只是把指针所只是把指针所指向的内存给释放掉指向的内存给释放掉,它,它并没有使指针本身消失。并没有使指针本身消失。pdelete以后,以后,指针还是指向其原来指向的内存指针还是指向其原来指向的内存空间空间,指针变量的值并未改变。,指针变量的值并未改变。p指针所指向的内存空间的内容已经释放掉了,指针所指向的内存空间的内容已经释放掉了,其内容变得毫无意义了,是垃圾,其内容变得毫无意义了,是垃圾,指针本身被指针本身被悬挂了悬挂了。p此时用语句此时用语句if(p!=NULL)进行防错处理是毫进行防错处理是毫无意义的无意义的.6.3 动态内存管理技术动态内存管理技术 2022-1
50、2-562p在例在例6_10中,安排了这种悬挂所带来的问题中,安排了这种悬挂所带来的问题的示例程序。的示例程序。p例例6_10:delete对内存干了什么?对内存干了什么?psmain6_10.cpp plpszChar和和lpszChar2指向了同一片动态内存指向了同一片动态内存空间,空间,delete以后,内存的内容毫无意义,但以后,内存的内容毫无意义,但是是lpszChar和和lpszChar2指针变量的值并没有指针变量的值并没有改变,还指向原来的位置。改变,还指向原来的位置。6.3 动态内存管理技术动态内存管理技术 2022-12-563plpszChar2:屯屯屯屯屯屯屯屯屯屯屯屯屯