第 7 章(1)━━动态内存分配.ppt_第1页
第 7 章(1)━━动态内存分配.ppt_第2页
第 7 章(1)━━动态内存分配.ppt_第3页
第 7 章(1)━━动态内存分配.ppt_第4页
第 7 章(1)━━动态内存分配.ppt_第5页
已阅读5页,还剩26页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、C+程序设计,第7章(1) 动态内存分配,主要内容,C+的四个内存区域 动态存储分配 堆内存 new 与 delete 运算符 动态存储分配的几点说明 使用 new 和 delete 的几点说明 动态创建对象 对象的浅拷贝、浅赋值 对象的深拷贝、深赋值 顺序表类模板的定义,C+的四个内存区域,C+的四个内存区域: 代码区存放程序代码。 静态数据区存放全局变量或对象、static局部变量或对象。全局变量或对象在程序开始运行时在该区分配; static局部变量或对象在程序运行过程中第一次进入其作用域时在该区分配。该区的变量或对象直到程序运行结束才被释放。 局部数据区(栈区) 存放auto局部变量或

2、对象 。 auto局部变量或对象在程序运行到其作用域时在栈区分配,但怎样分配在编译时就已经确定。auto局部变量或对象在离开其作用域时即被释放。 动态存储区(自由存储区、堆区) 存放程序运行过程中由 new 运算符动态创建的变量或对象 。动态创建的变量或对象在编译时无法为其预定存储空间,系统根据运行时的具体要求在该区分配。该区的变量或对象需要使用 delete 运算符才能将其释放。,动态存储分配,静态存储分配: 通常程序中定义的变量或对象,编译器在编译时就可根据该变量或对象的类型知道其所需内存空间的大小,即怎样分配在编译时就已经能够确定,从而在适当的时候为它们分配确定的内存空间,这种内存分配称

3、为静态存储分配。 动态存储分配: 有些变量或对象只有在程序运行过程中才能确定其大小,这样编译器在编译时就无法为它们预定内存空间,只能在程序运行过程中,系统根据运行时的具体要求进行内存分配,称为动态存储分配。动态存储分配是在自由存储区(堆区)中进行。,堆内存,关于堆内存: 堆区是由操作系统直接管理的内存区域,它是“公共的区域”,而且“面积”比较大,它的“生命周期”也不是由编译系统控制的,而是由编程的人控制的。在许多的应用中,堆内存的使用都是必不可少的。必须通过指针,才可以使用堆内存。 堆内存的分配与释放: 在程序运行过程中,遇到一个需要动态分配的变量或对象时,必须向系统申请取得堆内存中的一块所需

4、大小的内存空间,用于存放该变量或对象。当不再使用该变量或对象时,必须结束它的生命期,需要显式释放其所占用的内存空间。C+中,取得和释放堆内存中的空间,分别通过 new 和 delete 运算符来完成。new 用来动态分配内存空间, delete 用来将动态分配得到的内存空间归还给系统(释放)。,new 与 delete 运算符,new 运算符动态申请堆空间 格式1: 指针变量 = new 类型名 ; 格式2: 指针变量 = new 类型名 ( 初始值 ) ; 功能:动态创建一个指定类型的变量或对象。若创建成功(堆空间申请成功),new 运算返回新分配堆空间的首地址;若创建失败(堆空间申请失败)

5、, new 运算返回 0 或 NULL(空指针)。 delete 运算符动态释放堆空间 格式: delete 指针 ; 功能:释放由 new 运算动态创建的变量或对象。指针的值必须是 new 运算所分配堆空间的首地址,即 new 运算的返回值。,new 与 delete 运算符,new 运算符动态创建一维数组 格式: 指针变量 = new 类型名 下标表达式 ; 功能:动态创建一个指定类型的一维数组,数组元素的个数为下标表达式的值。若创建成功(堆空间申请成功),new 运算返回新分配一维数组空间的首地址;若创建失败(堆空间申请失败), new 运算返回 0 或 NULL(空指针)。 注意:动态

6、为数组分配堆空间时,不能同时进行初始化。 delete 运算符动态释放一维数组 格式: delete 指针 ; 功能:释放由 new 运算动态创建的一维数组。指针的值必须是 new 运算所分配一维数组空间的首地址,且前面必须加上一对方括号 “ ”。 注意:若 delete 中的方括号“ ”不写,只是释放一维数组中一个元素的空间。,【例】(动态创建变量) #include void main ( ) int * p = new int ( 100 ) ; cout void main ( ) int * p = new int 6 ; cout p i ; for ( i=0 ; i6 ; i+

7、 ) cout *( p + i ) t ; cout endl ; delete p ; ,运行: 请输入六个整数: 5 6 3 4 9 1 5 6 3 4 9 1,运行: 100 200,new 与 delete 运算符,new 运算符动态创建二维数组 格式: 指针变量 = new 类型名 下标表达式1 下标表达式2 ; 功能:动态创建一个指定类型的二维数组,数组元素的个数为各维下标表达式值的乘积。若创建成功(堆空间申请成功),new 运算返回新分配二维数组空间的首地址;若创建失败(堆空间申请失败), new 运算返回 0 或 NULL(空指针)。 注意:new 运算创建二维数组时,必须将

8、返回的地址赋给行指针变量;若要赋给元素指针变量,必须进行类型强制转换。 delete 运算符动态释放二维数组 格式: delete 指针 ; 功能:释放由 new 运算动态创建的二维数组。指针的值必须是 new 运算所分配二维数组空间的首地址,且前面必须加上一对方括号 “ ”。 注意:若 delete 中的方括号“ ”不写,只是释放二维数组中的一行元素的空间。,【例】(动态创建二维数组) #include void main ( ) float ( *p1 ) 3 = new float 2 3 ; int i , j ; cout p1 i j ; for ( i=0 ; i p2 i ;

9、for ( i=0 ; i6 ; i+ ) cout * ( p2+i ) t ; if ( ( i+1 ) %3 =0 ) cout endl ; delete p1 ; delete p2 ; ,运行: 请输入六个整数:5 6 3 4 9 1 5 6 3 4 9 1 请输入六个整数:5 6 3 4 9 1 5 6 3 4 9 1,动态存储分配的几点说明,动态分配失败: 动态分配是在堆内存中进行,但堆区资源有限,动态分配可能失败,这时 new 运算返回一个空指针(0或NULL),表示发生异常,堆区资源不足,分配失败。 指针删除与堆空间释放: delete 指针p ;其实际含义是删除指针p所指

10、向的变量或对象,即释放p所指向的堆空间,而不是删除指针p本身,释放该堆空间后,指针p就成了空悬指针(即指针p未指向一个有效的地址,而空悬指针是程序错误的一个根源),此时最好将指针p置空(0或NULL)。 动态数组的撤消: delete 指针p ;注意“ ”不可缺少,否则回收不彻底! 若释放由 new 创建的一维数组,而 delete 中又少了“ ”,由于指针p指向的是一维数组的首元素,是元素地址,此时只回收了首元素所占的堆空间。 若释放由 new 创建的二维数组,而 delete 中又少了“ ”,由于指针p指向的是二维数组的首行,是行地址,此时只回收了首行元素所占的堆空间。,动态存储分配的几点

11、说明,内存泄漏与重复释放: new 和 delete 是配对使用的,若 new 返回的指针值丢失,其所分配的堆空间将无法回收,称为内存泄漏。程序执行结束后,这部分空间将从系统中丢失,必须重新启动计算机才能找回。 对同一个堆空间重复释放也是危险的,因为可能在第一次释放后,该空间已经分配给其它变量或对象,再次释放会使新的变量或对象丢失。 动态分配的变量或对象的生存期: 非动态分配的变量或对象,在定义时都要用标识符命名,称命名变量或对象。auto局部的命名变量或对象其生存期在作用域内;全局和static局部的命名变量或对象在程序运行结束才被自动释放。 动态分配的变量或对象没有名字,对其访问只能通过指

12、针来间接进行,称无名变量或对象,其生存期不依赖于建立它的作用域。例如,在被调函数中动态分配的变量或对象,只要不被 delete 释放,在返回主调函数后仍可使用,但前提是其起始地址必须从被调函数中带出,否则将无法访问。,使用 new 和 delete 的几点说明,使用 new 动态分配的存储空间,若不赋初值,其初值不确定。 若 new 动态分配的空间较大,通常需要判断其返回的指针值是否为0或NULL,若为0或NULL ,表示动态分配失败,此时应终止程序执行或进行出错处理。 【例】 int ( *p ) 200 = new int 100 200 ; if ( p = 0 ) exit ( 0 )

13、 ; 或 if ( p = NULL ) exit ( 0 ) ; 用 new 为数组动态分配堆空间,或为结构体动态分配堆空间,不能同时初始化。 【例】 int *p = new int 5 1 , 3 , 5 , 7 , 9 ; 【例】 Student *p = new Student 111, “张军”, 99 ; 【例】 Student *p = new Student ( 111, “张军”, 99 ); 必须妥善保存 new 动态分配空间的指针值,以便其后用 delete 来归还该动态分配的空间。一旦 delete 释放了指针所指向的堆空间,就不能再对其赋值,否则可能出现不可预测的错

14、误。,动态创建对象,使用 new 运算符动态创建对象: 对象指针变量 = new 类名 ( 初值列表 ); new 运算符动态创建对象时,首先在堆区为该对象分配一块存储空间,然后自动调用相应的构造函数来初始化其成员数据,并返回该对象的起始地址。 注意:new 运算符动态创建的对象没有名字,仅返回对象的起始地址,必须将该地址保存在对象指针变量中,然后通过对象指针访问该对象的成员。 使用 delete 运算符释放动态创建的对象: delete 对象指针 ; delete 运算符在释放对象空间前会自动调用析构函数。 注意: new 运算符动态创建的对象,若不使用 delete 运算符来释放其空间,程

15、序运行结束该对象仍然存在,占用相应的存储空间,系统也不会自动调用析构函数。,【例】( 学生类、new创建动态对象, delete释放动态对象) # include # include class Student int id ; char name 8 ; int score ; public: Student ( int i=0, char *na=0, int s=0 ) id = i ; strcpy( name, na ) ; score = s ; cout print( ) ; delete p1 ; Student *p2 = new Student ( 222 , “王红” ,

16、 88 ) ; p2-print( ); /注意:此处漏写 delete p2; ,运行: 对象地址=4390992 构造 ( 张军 ) 了! 学号=111 姓名=张军 成绩=99 对象地址=4390992 析构 ( 张军 ) 了! 对象地址=4390992 构造 ( 王红 ) 了! 学号=222 姓名=王红 成绩=88,【例】( 点类、new创建动态对象数组,delete释放动态对象数组) # include # include class Point float x , y ; public: Point ( float a=0 , float b=0 ) x = a ; y = b ;

17、cout x y ; void print ( ) cout input( ) ; for ( i=0 ; i2 ; i+ ) for ( j=i+1 ; j3 ; j+ ) p i .print( ) ; cout “与” ; p j .print( ) ; cout “的距离=” p i .calculate( p j ) endl ; delete p ; ,对象的浅拷贝、浅赋值,对象的浅拷贝、浅赋值: 创建新对象时,若使用本类已有的对象来初始化新对象,系统将自动调用拷贝构造函数来实现对象的拷贝。值得注意的是:系统给出的默认拷贝构造函数是各成员的依次拷贝,而不是对象整体的按位拷贝。通常这

18、是没有问题的。 同类的两个对象之间进行赋值“=”运算时,系统将自动调用赋值“=”运算符重载函数operator=( ) 来实现对象的赋值。值得注意的是:系统给出的默认operator=( )重载函数是各成员的依次赋值,而不是对象整体的按位赋值。通常也是没有问题的。,对象的浅拷贝、浅赋值,对象的浅拷贝(或浅赋值)不适用的情况:若类中有成员数据为指针p,且该类的某个对象ob1中的该成员数据p,指向了动态分配堆区的一个变量或对象,那么若将该对象ob1浅拷贝(或浅赋值)给本类的另一个对象ob2,则对象ob1是按成员依次拷贝(或赋值)给对象ob2,这时 ob1.p 和 ob2.p 将指向堆区的同一个变量

19、或对象。 【例】(对象浅拷贝或浅赋值 ob2 = ob1 ),对象的浅拷贝、浅赋值,上例中,当浅拷贝(或浅赋值)的对象析构时,会引起下列问题: 若使用系统给出的默认析构函数,则动态分配堆区的对象不能回收。如左下图。 若类中自定义析构函数,且在析构函数中用 “ delete p ” 将堆区的对象先释放,则在析构对象ob2时,堆区的那个对象先被其释放,以后再析构对象ob1时,堆区的那个对象就出现了二次释放的问题。如右下图。 【例】(对象的析构 ),【例】( 浅拷贝、浅赋值 ) # include # include class mystring char *ps ; /指向动态分配在堆区的字符串

20、public: mystring ( char *s=0 ) if ( s ) ps = new char strlen( s ) + 1 ; strcpy( ps , s ) ; else ps = 0 ; void show( ) if ( ps ) cout ps ; cout endl ; ; void main( ) mystring s1 ( “教师” ) , s2 ( s1 ) , s3 ( “人民子弟兵” ) ; cout “s1=” ; s1.show( ) ; cout “s2=” ; s2.show( ) ; cout “s3=” ; s3.show( ) ; s3 =

21、s2 ; cout “s3=” ; s3.show( ) ; ,运行: s1= 教师 s2 = 教师 s3 = 人民子弟兵 s3 = 教师 程序运行正常退出 ,但 堆区内存发生泄漏。,【例】( 浅拷贝、浅赋值、自定义析构函数 ) # include # include class mystring char *ps ; /指向动态分配在堆区的字符串 public: mystring ( char *s=0 ) if ( s ) ps = new char strlen( s ) + 1 ; strcpy( ps , s ) ; else ps = 0 ; mystring ( ) if ( p

22、s ) delete ps ; /自定义析构函数 void show( ) if ( ps ) cout ps ; cout endl ; ; void main( ) mystring s1 ( “教师” ) , s2 ( s1 ) , s3 ( “人民子弟兵” ) ; cout “s1=” ; s1.show( ) ; cout “s2=” ; s2.show( ) ; cout “s3=” ; s3.show( ) ; s3 = s2 ; cout “s3=” ; s3.show( ) ; ,运行: s1= 教师 s2 = 教师 s3 =人民子弟兵 s3 = 教师 程序运行发生异常 ,因

23、 为堆区内存重复释放。,对象的深拷贝、深赋值,对深拷贝、深赋值的理解和实现: 若类中有成员数据为指针p,且该类的某个对象ob1中的该成员数据p,指向了动态分配堆区的一个变量或对象,那么若将该对象ob1深拷贝(或深赋值)给本类的另一个对象ob2,则不是拷贝(或赋值)该成员数据p本身,而是对指针p所指的堆区对象进行拷贝(或赋值) 。 需要重新自定义拷贝构造函数、赋值“=”运算符重载函数、以及析构函数,使得每个对象的成员数据p 都各自指向堆区一个独立的变量或对象。 深拷贝 ob2 ( ob1 ) 时,先为新对象ob2中指针p 单独分配一个堆区对象空间,再将对象ob1中指针p所指的堆区对象拷贝给新对象

24、ob2中指针p所指的堆区对象。,对象的深拷贝、深赋值,深赋值 ob2 = ob1 时,需先将被赋值对象ob2中指针p原先所指的堆区对象释放,再为对象ob2中指针p单独分配一个堆区对象空间,然后将对象ob1中指针p所指的堆区对象赋值给对象ob2中指针p所指的堆区对象。 释放对象ob1、ob2之前,需要先将其成员数据p所指的堆区对象释放,释放动态分配资源由析构函数完成。 【例】(对象深拷贝或深赋值 ob2 = ob1 ),【例】( 深拷贝、深赋值,自定义拷贝构造、析构函数、赋值“=”运算符重载函数 ) # include # include class mystring char *ps ; /指

25、向动态分配在堆区的字符串 public: mystring ( char *s=0 ) /用C风格字符串来初始化mystring类的对象 if ( s ) ps = new char strlen( s ) + 1 ; strcpy( ps , s ) ; else ps = 0 ; mystring ( mystring ,运行: s1= 教师 s2 = 教师 s3 =人民子弟兵 s3 = 教师, 创建对象 s1、s2、s3 : 撤消对象 s1、s2、s3 : 赋值运算 s3 = s2 :,“人民子弟兵” (堆区),定义一个顺序表类模板Array,要求: 顺序表中存放元素的数组容器空间开在堆

26、区,首地址存放在Array类的成员数据中。 将新元素加入顺序表中时,若顺序表的数组容器空间已满,必须动态创建一个是原容器空间两倍的新容器空间,然后将原容器空间中的元素移入新容器空间中,并撤消原容器空间,再将新元素加入到新容器空间中。) 类模板 Array 的模板参数: typename T 类型参数T,在类中代表顺序表中元素的数据类型。 成员数据: T *slist ; /开在堆区的数组容器空间的起始地址。 int size ; /堆区数组容器中所能存放元素的最大个数。 int last ; /堆区数组容器中已存放元素的个数,空表时 last = 0,顺序表类模板的定义,成员函数: 构造函数: Array ( int max=10 ) ; /默认构造 Array (

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论