第9讲动态内存分配736304930.ppt

上传人:本田雅阁 文档编号:3137974 上传时间:2019-07-16 格式:PPT 页数:41 大小:931.02KB
返回 下载 相关 举报
第9讲动态内存分配736304930.ppt_第1页
第1页 / 共41页
第9讲动态内存分配736304930.ppt_第2页
第2页 / 共41页
第9讲动态内存分配736304930.ppt_第3页
第3页 / 共41页
第9讲动态内存分配736304930.ppt_第4页
第4页 / 共41页
第9讲动态内存分配736304930.ppt_第5页
第5页 / 共41页
点击查看更多>>
资源描述

《第9讲动态内存分配736304930.ppt》由会员分享,可在线阅读,更多相关《第9讲动态内存分配736304930.ppt(41页珍藏版)》请在三一文库上搜索。

1、第九讲 动态内存分配,教材:C+语言程序设计(第4版) 第6章 6.36.5、6.8,清华大学 郑 莉,目录,9.1 动态内存分配 9.2 用vector创建数组对象 9.3 深拷贝与浅拷贝 9.4 深度探索 9.5 小结,2,3,9.1 动态内存分配,动态申请内存操作符 new,new 类型名T(初始化参数列表) 功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值。 结果值:成功:T类型的指针,指向新分配的内存;失败:抛出异常。,4,9.1 动态内存分配,释放内存操作符delete,delete 指针p 功能:释放指针p所指向的内存。p必须是new操作的返回值。,例

2、9-1(教材例6-16) 动态创建对象举例,5,#include using namespace std; class Point public: Point() : x(0), y(0) cout“Default Constructor called.“endl; Point(int x, int y) : x(x), y(y) cout “Constructor called.“endl; Point() cout“Destructor called.“endl; int getX() const return x; int getY() const return y; void move

3、(int newX, int newY) x = newX; y = newY; private: int x, y; ;,9.1 动态内存分配,例9-1 (续),6,int main() cout “Step one: “ endl; Point *ptr1 = new Point;/调用缺省构造函数 delete ptr1; /删除对象,自动调用析构函数 cout “Step two: “ endl; ptr1 = new Point(1,2); delete ptr1; return 0; ,9.1 动态内存分配,运行结果: Step One: Default Constructor c

4、alled. Destructor called. Step Two: Constructor called. Destructor called.,7,9.1 动态内存分配,申请和释放动态数组,分配:new 类型名T 数组长度 数组长度可以是任何表达式,在运行时计算 释放:delete 数组名p 释放指针p所指向的数组。p必须是用new分配得到的数组首地址。,例9-2(教材例6-17) 动态创建对象数组举例,8,#include using namespace std; class Point /类的声明同例6-16,略 ; int main() Point *ptr = new Point

5、2; /创建对象数组 ptr0.move(5, 10); /通过指针访问数组元素的成员 ptr1.move(15, 20); /通过指针访问数组元素的成员 cout “Deleting.“ endl; delete ptr; /删除整个对象数组 return 0; ,9.1 动态内存分配,9,例9-2 (续),运行结果: Default Constructor called. Default Constructor called. Deleting. Destructor called. Destructor called.,9.1 动态内存分配,10,9.1 动态内存分配,将动态数组封装成类

6、,更加简洁,便于管理 建立和删除数组的过程比较繁琐 封装成类后更加简洁,便于管理 可以在访问数组元素前检查下标是否越界 用assert来检查,assert只在调试时生效,例9-3(教材例6-18)动态数组类,11,#include #include using namespace std; class Point /类的声明同例6-16 ; class ArrayOfPoints /动态数组类 public: ArrayOfPoints(int size) : size(size) points = new Pointsize; ArrayOfPoints() cout = 0 ,9.1 动态

7、内存分配,例9-3 (续),12,int main() int count; cout count; ArrayOfPoints points(count); /创建对象数组 /通过访问数组元素的成员 points.element(0).move(5, 0); /通过类访问数组元素的成员 points.element(1).move(15, 20); return 0; ,9.1 动态内存分配,13,例9-3 (续),运行结果: Please enter the number of points:2 Default Constructor called. Default Constructor

8、 called. Deleting. Destructor called. Destructor called.,9.1 动态内存分配,14,9.1 动态内存分配,动态创建多维数组,new 类型名T第1维长度第2维长度; 如果内存申请成功,new运算返回一个指向新分配内存首地址的指针,是一个T类型的数组,数组元素的个数为除最左边一维外各维下标表达式的乘积。 例如: char (*fp)3; fp = new char23;,15,9.1 动态内存分配,char (*fp)3;,fp,fp+1,例9-4(教材例6-19) 动态创建多维数组,16,#include using namespace

9、std; int main() float (*cp)98 = new float898; for (int i = 0; i (i * 100 + j * 10 + k);,9.1 动态内存分配,例9-4 (续),17,for (int i = 0; i 8; i+) for (int j = 0; j 9; j+) for (int k = 0; k 8; k+) /将指针cp作为数组名使用,通过数组名和下标访问数组元素 cout cpijk “ “; cout endl; cout endl; delete cp; return 0; ,9.1 动态内存分配,18,用vector创建数组

10、对象,为什么需要vector? 将动态数组封装,自动创建和删除 数组下标越界检查 例6-18中封装的ArrayOfPoints也提供了类似功能,但只适用于一种类型的数组 vector动态数组对象的定义 vector 数组对象名(数组长度); 例:vector arr(5) 建立大小为5的int数组,9.2 用vector创建数组对象,19,9.2 用vector创建数组对象,vector数组对象的使用,对数组元素的引用 与普通数组具有相同形式: 数组对象名 下标表达式 但vector数组对象名不表示数组首地址 获得数组长度 用size函数 数组对象名.size(),例9-5(教材例6-20)v

11、ector应用举例,20,#include #include using namespace std; /计算数组arr中元素的平均值 double average(const vector ,9.2 用vector创建数组对象,例9-5 (续),21,int main() unsigned n; cout n; vector arr(n); /创建数组对象 cout arri; cout “Average = “ average(arr) endl; return 0; ,9.2 用vector创建数组对象,22,9.3 深拷贝与浅拷贝,深拷贝与浅拷贝,浅拷贝 实现对象间数据元素的一一对应复

12、制。 深拷贝 当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制。,例9-6(教材例6-21)对象的浅拷贝,23,#include #include using namespace std; class Point /类的声明同例6-16 / ; class ArrayOfPoints /类的声明同例6-18 / ;,9.3 深拷贝与浅拷贝,例9-6 (续),24,int main() int count; cout count; ArrayOfPoints pointsArray1(count); /创建对象数组 pointsArray1.element(

13、0).move(5,10); pointsArray1.element(1).move(15,20); ArrayOfPoints pointsArray2 = pointsArray1; /创建副本 cout “Copy of pointsArray1:“ endl; cout “Point_0 of array2: “ pointsArray2.element(0).getX() “, “ pointsArray2.element(0).getY() endl; cout “Point_1 of array2: “ pointsArray2.element(1).getX() “, “ p

14、ointsArray2.element(1).getY() endl;,9.3 深拷贝与浅拷贝,例9-6 (续),25,pointsArray1.element(0).move(25, 30); pointsArray1.element(1).move(35, 40); cout “After the moving of pointsArray1:“ endl; cout “Point_0 of array2: “ pointsArray2.element(0).getX() “, “ pointsArray2.element(0).getY() endl; cout “Point_1 of

15、array2: “ pointsArray2.element(1).getX() “, “ pointsArray2.element(1).getY() endl; return 0; ,9.3 深拷贝与浅拷贝,例9-6 (续),26,运行结果如下: Please enter the number of points:2 Default Constructor called. Default Constructor called. Copy of pointsArray1: Point_0 of array2: 5, 10 Point_1 of array2: 15, 20 After the

16、 moving of pointsArray1: Point_0 of array2: 25, 30 Point_1 of array2: 35, 40 Deleting. Destructor called. Destructor called. Deleting. 接下来程序出现异常,也就是运行错误。,9.3 深拷贝与浅拷贝,27,9.3 深拷贝与浅拷贝,拷贝前,拷贝后,例9-6 对象的深拷贝,28,#include #include using namespace std; class Point /类的声明同例6-16 ; class ArrayOfPoints public: Arr

17、ayOfPoints(const ArrayOfPoints int main() /同例6-20 ,9.3 深拷贝与浅拷贝,例9-6 (续),29,程序的运行结果如下: Please enter the number of points:2 Default Constructor called. Default Constructor called. Default Constructor called. Default Constructor called. Copy of pointsArray1: Point_0 of array2: 5, 10 Point_1 of array2:

18、15, 20 After the moving of pointsArray1: Point_0 of array2: 5, 10 Point_1 of array2: 15, 20 Deleting. Destructor called. Destructor called. Deleting. Destructor called. Destructor called.,9.3 深拷贝与浅拷贝,30,9.3 深拷贝与浅拷贝,拷贝前,拷贝后,指针与引用的对应关系,/使用指针常量 void swap(int * const pa, int * const pb) int temp = *pa;

19、*pa = *pb; *pb = temp; int main() int a, b; swap( ,/使用引用 void swap(int ,31,9.4 深度探索 9.4.1 指针与引用,指针与引用的联系,引用在底层通过指针来实现 一个引用变量,通过存储被引用对象的地址,来标识它所引用的对象 引用是对指针的包装,比指针更高级 指针是C语言就有的底层概念,使用起来很灵活,但用不好容易出错 引用隐藏了指针的“地址”概念,不能直接对地址操作,比指针更安全,32,9.4 深度探索 9.4.1 指针与引用,引用与指针的选择,什么时候用引用? 如无需直接对地址进行操作,指针一般都可用引用代替 用更多的

20、引用代替指针,更简洁、安全 什么时候用指针? 引用的功能没有指针强大,有时不得不用指针: 引用一经初始化,无法更改被引用对象,如有这种需求,必须用指针; 没有空引用,但有空指针,如果空指针有存在的必要,必须用指针; 函数指针; 用new动态创建的对象或数组,用指针存储其地址最自然; 函数调用时,以数组形式传递大量数据时,需要用指针作为参数。,33,9.4 深度探索 9.4.1 指针与引用,指针的地址安全性问题,地址安全性问题 通过指针,访问了不该访问的地址,就会出问题 典型问题:数组下标越界 问题的严重性:有时会在不知不觉中产生错误,错误源很难定位,因此程序调试起来很麻烦 解决方案 指针只有赋

21、了初值才能使用(这一点普通变量也应遵循) 指针的算术运算,一定要限制在通过指向数组中某个元素的指针,得到指向同一个数组中另一个元素的指针 尽量使用封装的数组(如vector),而不直接对指针进行操作,34,9.4 深度探索 9.4.2 指针的安全性隐患及其应对方案,指针的类型安全性问题,基本类型数据的转换是基于内容的: 例: int i = 2; float x = static_cast(i); 目标类型不同的指针间的转换,会使一种类型数据的二进制序列被当作另一种类型的数据: reinterpret_cast:可以将一种类型的指针转换为另一种类型 int i = 2; float *p =

22、reinterpret_cast( 结论 从一种具体类型指针转换为另一种具体类型指针的reinterpret_cast很不安全,一般情况下不要用,35,9.4 深度探索 9.4.2 指针的安全性隐患及其应对方案,指针的类型安全性问题(续),void指针与具体类型指针的转换: 具体类型指针可以隐含地转换为void指针: int i = 2; void *vp = 结论 void指针也容易带来不安全,尽量不用,在不得不用的时候,一定要在将void指针转换为具体类型指针时,确保指针被转换为它最初的类型。,36,9.4 深度探索 9.4.2 指针的安全性隐患及其应对方案,堆对象的管理,堆对象必须用de

23、lete删除 避免“内存泄漏” 原则很简单,但在复杂的程序中,一个堆对象常常要被多个不同的类、模块访问,该在哪里删除,常常引起混乱 如何有条理地管理堆对象 明确每个堆对象的归属 最理想的情况:在一个类的成员函数中创建的堆对象,也在这个类的成员函数中删除 把对象的建立和删除变成了一个类的局部问题 如需在不同类之间转移堆对象的归属,一定要在注释中注明,作为类的对外接口约定 例如在一个函数内创建堆对象并将其返回,则该对象应当由调用该函数的类负责删除,37,9.4 深度探索 9.4.2 指针的安全性隐患及其应对方案,const_cast的应用,const_cast的用法 用于将常指针、常引用转换为不带

24、“const”的相关类型 例: void foo(const int *cp) int *p = const_cast(cp); (*p)+; 这可以通过常指针修改指针发对象的值,但这是不安全的用法,因为破坏了接口中const的约定 const_cast不是安全的转换,但有时可以安全地使用,38,9.4 深度探索 9.4.3 const_cast的应用,const_cast的安全使用,对例6-18的改进 例6-18通过下列函数访问数组元素 Point 新问题 代码的重复:这个element函数与原先的函数内容完全相同,两份重复的代码,不易维护,39,9.4 深度探索 9.4.3 const_c

25、ast的应用,const_cast的安全使用(续),新问题的解决 修改原先的element函数: Point 执行过程:调用常成员函数element(),再将其返回结果中的const用const_cast去除 将this用static_cast转换为常指针,是安全的转换 该函数本身不是常成员函数,确保将最终的结果以普通引用形式返回是安全的 思考:如果保留该函数,而修改常成员函数element,使常成员函数element调用该函数,是否合适?,40,9.4 深度探索 9.4.3 const_cast的应用,小结,41,9.5 小结,主要内容 动态存储分配、用vector创建数组对象、深拷贝与浅拷贝。 达到的目标 理解和掌握动态存储分配技术,会使用vector类,理解对象浅拷贝愈深拷贝的差别,会实现深拷贝。,

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

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1