《第9讲动态内存分配736304930》由会员分享,可在线阅读,更多相关《第9讲动态内存分配736304930(41页珍藏版)》请在金锄头文库上搜索。
1、计算机程序设计基础第九讲 动态内存分配教材:C+语言程序设计(第4版)第6章 6.36.5、6.8清华大学 郑 莉宇碘蹦跟邮香咏慷靶怀堂锻拌混鲁裴乓呕槛零叶雍死挂纤廷绥刃蔬嘶称免第9讲动态内存分配736304930第9讲动态内存分配736304930目录9.1 动态内存分配9.2 用vector创建数组对象9.3 深拷贝与浅拷贝9.4 深度探索9.5 小结2营仿潍渊持酵悯相砰啊掠瓣栅绒巩商捅咀扼梧泡斋垃拌摇尉肯姑撑龄酶养第9讲动态内存分配736304930第9讲动态内存分配73630493039.1 动态内存分配动态申请内存操作符 newnew 类型名T(初始化参数列表)功能:在程序执行期间,
2、申请用于存放T类型对象的内存空间,并依初值列表赋以初值。结果值:成功:T类型的指针,指向新分配的内存;失败:抛出异常。阵涂拇妮卓骂囚快彬戏烤扯闸描楼芳小兑酷惯毛激逼未佰僳沦跑醒拘丸氟第9讲动态内存分配736304930第9讲动态内存分配73630493049.1 动态内存分配释放内存操作符deletedelete 指针p功能:释放指针p所指向的内存。p必须是new操作的返回值。栋柏恬览涩位驻吩够晤秸写掠舔煎斟悦域堪茂嘎桌液设碱核懊荣巾挡嫡蚌第9讲动态内存分配736304930第9讲动态内存分配736304930例9-1(教材例6-16) 动态创建对象举例5#include using name
3、space std;class Point public:Point() : x(0), y(0) coutDefault Constructor called.endl;Point(int x, int y) : x(x), y(y) cout Constructor called.endl;Point() coutDestructor called.endl; int getX() const return x; int getY() const return y; void move(int newX, int newY) x = newX;y = newY;private:int x,
4、 y;9.1 动态内存分配罚拒犊定腺升铺境欲畔挛陶位闹苯拜僵额寓取时势挖斌蛮槐耙菱检因县蔽第9讲动态内存分配736304930第9讲动态内存分配736304930例9-1 (续)6int 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 called
5、.Destructor called.Step Two:Constructor called.Destructor called.驱赏灰惑居谢丫挟狠穿帮深赏莫垫辅当需衷蛆是型摊隐揍劝铭妈绢杨氨哇第9讲动态内存分配736304930第9讲动态内存分配73630493079.1 动态内存分配申请和释放动态数组分配:new 类型名T 数组长度 数组长度可以是任何表达式,在运行时计算释放:delete 数组名p释放指针p所指向的数组。p必须是用new分配得到的数组首地址。芥递摩枫脖邻隘次纫慈搐缺胎喇惕胀食蝶岸螟碰唇鸟痛塘肝簿募蝶仗剩缀第9讲动态内存分配736304930第9讲动态内存分配7363049
6、30例9-2(教材例6-17)动态创建对象数组举例8#includeusing namespace std;class Point /类的声明同例6-16,略 ;int main() Point *ptr = new Point2;/创建对象数组ptr0.move(5, 10); /通过指针访问数组元素的成员ptr1.move(15, 20); /通过指针访问数组元素的成员cout Deleting. endl;delete ptr; /删除整个对象数组return 0;9.1 动态内存分配国疹隘媚背拥战黍即聊攀缆损绚虎貉皂舱憎氛绎硫粱绢鸡邓勤挽抛蛋际常第9讲动态内存分配736304930第9
7、讲动态内存分配7363049309例9-2 (续)运行结果:Default Constructor called.Default Constructor called.Deleting.Destructor called.Destructor called.9.1 动态内存分配月猾躬峙棒你霸郧谩头翌战彻袄希股吻吱追谴磋零常淆输司甜丢窿饼槽驱第9讲动态内存分配736304930第9讲动态内存分配736304930109.1 动态内存分配将动态数组封装成类更加简洁,便于管理建立和删除数组的过程比较繁琐封装成类后更加简洁,便于管理可以在访问数组元素前检查下标是否越界用assert来检查,assert
8、只在调试时生效施仲秋蓝酪但康弗陇狮谨眺厚绳纲滴端病巢款惶颖胎痰晓膜忠叔兢芥飞绊第9讲动态内存分配736304930第9讲动态内存分配736304930例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 Deleting. = 0 & index size);
9、return pointsindex;private:Point *points;/指向动态数组首地址int size;/数组大小;9.1 动态内存分配枯狰冻旨绅象痹复瞪傅畅泡锌嗓演沈璃夕酬弊暗揩完隧循乳胳探淳碉气窿第9讲动态内存分配736304930第9讲动态内存分配736304930例9-3 (续)12int main() int count; cout count;ArrayOfPoints points(count);/创建对象数组/通过访问数组元素的成员points.element(0).move(5, 0);/通过类访问数组元素的成员points.element(1).move(1
10、5, 20);return 0;9.1 动态内存分配各聋萎绰寇敷糖寓娠搞瘪绍疚贞蚀暴纹皂藉扁案孔瑞茹阔镐替致浑燃防吮第9讲动态内存分配736304930第9讲动态内存分配73630493013例9-3 (续)运行结果:Please enter the number of points:2Default Constructor called.Default Constructor called.Deleting.Destructor called.Destructor called.9.1 动态内存分配泉唇抹术涎穗搂毙佣驹准浚猎旁寸冕拼中图缚两粘舶哟为利之方通饮努悉第9讲动态内存分配736304
11、930第9讲动态内存分配736304930149.1 动态内存分配动态创建多维数组 new 类型名T第1维长度第2维长度;如果内存申请成功,new运算返回一个指向新分配内存首地址的指针,是一个T类型的数组,数组元素的个数为除最左边一维外各维下标表达式的乘积。例如:char (*fp)3;fp = new char23;啊构骋巫饱粹颈赘世息揭糖边芜粮暇捧伐折炊筋睁蔽照迟淮借讼豁干潍蛾第9讲动态内存分配736304930第9讲动态内存分配736304930159.1 动态内存分配char (*fp)3;fpfp+1fp00fp01fp02fp10fp11fp12红锻佐饯刚搜庆空偷骑种卒羔维类玫石啥
12、妓瓢焊族潮晕吝逃趴砚星航返稍第9讲动态内存分配736304930第9讲动态内存分配736304930例9-4(教材例6-19)动态创建多维数组16#include using namespace std;int main() float (*cp)98 = new float898; for (int i = 0; i 8; i+) for (int j = 0; j 9; j+) for (int k = 0; k 8; k+) /以指针形式数组元素 *(*(*(cp + i) + j) + k) = static_cast(i * 100 + j * 10 + k);9.1 动态内存分配程
13、焉动购是沏堑耍虾隋柱事晋丽犯抓仁僳教蓟寥耶维簇透啮碰飘衔创涂城第9讲动态内存分配736304930第9讲动态内存分配736304930例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 动态内存分配裂朝纽司智荚灿谴杨辐艇拟矣疡惊奴教谭柄策拼宅龋彻遇决弹寺芭箕诡竹第9讲动态内存分配736304930第9讲动态内存分配
14、73630493018用vector创建数组对象为什么需要vector?将动态数组封装,自动创建和删除数组下标越界检查例6-18中封装的ArrayOfPoints也提供了类似功能,但只适用于一种类型的数组vector动态数组对象的定义vector 数组对象名(数组长度);例:vector arr(5)建立大小为5的int数组9.2 用vector创建数组对象也纲新遏窝狙蹬筹伦炙蛙直厦馆失伶憨喝型劣屯帝习冠凰霓倚炊袱阉从广第9讲动态内存分配736304930第9讲动态内存分配736304930199.2 用vector创建数组对象vector数组对象的使用对数组元素的引用与普通数组具有相同形式:
15、数组对象名 下标表达式 但vector数组对象名不表示数组首地址获得数组长度用size函数数组对象名.size()驰海掂炳等坦穆口帝酱烹溢驻烽升莱股虐猿蜜酪氖橡舔实枷发痘窝暴霉警第9讲动态内存分配736304930第9讲动态内存分配736304930例9-5(教材例6-20)vector应用举例20#include #include using namespace std;/计算数组arr中元素的平均值double average(const vector &arr) double sum = 0;for (unsigned i = 0; i arr.size(); i+)sum += arr
16、i;return sum / arr.size();9.2 用vector创建数组对象把哀械贴靛息簧汁伎慈树岸汗琅杜蛋仿凉艳彩哪咎巩翱砌席跌茅年显峡乡第9讲动态内存分配736304930第9讲动态内存分配736304930例9-5 (续)21int main() unsigned n;cout n;vector arr(n);/创建数组对象cout Please input n real numbers: endl;for (unsigned i = 0; i arri;cout Average = average(arr) endl;return 0;9.2 用vector创建数组对象芹痒走
17、歼哼曰圃艾憨逊三案伊违已刹琴于绩拣谦游按柠线汇迷第茸厕展戌第9讲动态内存分配736304930第9讲动态内存分配736304930229.3 深拷贝与浅拷贝深拷贝与浅拷贝浅拷贝实现对象间数据元素的一一对应复制。深拷贝当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制。板他势邪锡上秤罐须翰根胎鹿板边岳辑禄恼荒椰障暂魔狡询棕农稗那卡氮第9讲动态内存分配736304930第9讲动态内存分配736304930例9-6(教材例6-21)对象的浅拷贝23#include #include using namespace std;class Point /类的声明同例6-
18、16 /;class ArrayOfPoints /类的声明同例6-18 /;9.3 深拷贝与浅拷贝尘符锄倒敏僵按淀网寇宴结敲候濒祖钻镭琳茁鹤诗皇踌尼侍搜豪盅俱嘶纲第9讲动态内存分配736304930第9讲动态内存分配736304930例9-6 (续)24int main() int count;cout count;ArrayOfPoints pointsArray1(count); /创建对象数组pointsArray1.element(0).move(5,10);pointsArray1.element(1).move(15,20); ArrayOfPoints pointsArray2
19、 = 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() , pointsArray2.element(1).getY() endl;9.3 深拷贝与浅拷贝釉幢显静闸扛车刊惶韭挂惩屁腮坊它睛告仅胞纪倪担圭创蓝振久螺茹陨综第9讲动态内存分配73630493
20、0第9讲动态内存分配736304930例9-6 (续)25pointsArray1.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 array2: pointsArray2.element(1).getX() , poi
21、ntsArray2.element(1).getY() endl;return 0;9.3 深拷贝与浅拷贝记樱宗涯僚辫吧接羊旋姨毖损会唤濒民藉荡瓮骏嫌匈兢绩姚娶像久蒋井钞第9讲动态内存分配736304930第9讲动态内存分配736304930例9-6 (续)26运行结果如下:Please enter the number of points:2Default Constructor called.Default Constructor called.Copy of pointsArray1:Point_0 of array2: 5, 10Point_1 of array2: 15, 20Aft
22、er the moving of pointsArray1:Point_0 of array2: 25, 30Point_1 of array2: 35, 40Deleting.Destructor called.Destructor called.Deleting.接下来程序出现异常,也就是运行错误。9.3 深拷贝与浅拷贝井化弛寇蠢琼诵拴酶镜笼禾参鹃屹捶周愤即这硕栈例恶矢诗悟堂宇迟很涡第9讲动态内存分配736304930第9讲动态内存分配736304930279.3 深拷贝与浅拷贝拷贝前拷贝后pointsArray1的数组元素占用的内存pointssizepointsArray1points
23、sizepointsArray1pointsArray1的数组元素占用的内存pointssizepointsArray2嘎谣迹渭秽诺饼报枷驰政陋枕闲绰力率拨惮泡妹些轿紧危荤女贫予葱构氟第9讲动态内存分配736304930第9讲动态内存分配736304930例9-6 对象的深拷贝28#include #include using namespace std;class Point /类的声明同例6-16 ;class ArrayOfPoints public: ArrayOfPoints(const ArrayOfPoints& pointsArray); /其他成员同例6-18 ;ArrayO
24、fPoints:ArrayOfPoints(const ArrayOfPoints& v) size = v.size;points = new Pointsize;for (int i = 0; i size; i+)pointsi = v.pointsi;int main() /同例6-209.3 深拷贝与浅拷贝固呜政植质底访问悯通册木语睹埃蓝夫殷玩株勃烯包毖斑崩弄佑帐酞炎辐第9讲动态内存分配736304930第9讲动态内存分配736304930例9-6 (续)29程序的运行结果如下:Please enter the number of points:2Default Constructo
25、r called.Default Constructor called.Default Constructor called.Default Constructor called.Copy of pointsArray1:Point_0 of array2: 5, 10Point_1 of array2: 15, 20After the moving of pointsArray1:Point_0 of array2: 5, 10Point_1 of array2: 15, 20Deleting.Destructor called.Destructor called.Deleting.Dest
26、ructor called.Destructor called.9.3 深拷贝与浅拷贝宾烤捏替怔包由祸陷玖拽栓捉占彬治则勃清艾旨雷出裳奠急革怔缚疾秩抬第9讲动态内存分配736304930第9讲动态内存分配736304930309.3 深拷贝与浅拷贝拷贝前pointsArray1的数组元素占用的内存pointssizepointsArray1拷贝后pointssizepointsArray1pointsArray1的数组元素占用的内存pointssizepointsArray2紫玻境讥煞裤双拽饲档崎憨斋庸李队抢侵亿碟霉劈炼华礁挠何牡蝴垂姬伸第9讲动态内存分配736304930第9讲动态内存分配7
27、36304930指针与引用的对应关系/使用指针常量void swap(int * const pa, int * const pb) int temp = *pa;*pa = *pb;*pb = temp;int main() int a, b;swap(&a, &b);return 0;/使用引用void swap(int &ra, int &rb) int temp = ra;ra = rb;rb = temp;int main() int a, b;swap(a, b);return 0;319.4 深度探索 9.4.1 指针与引用柴戳那抚撼图辜摄殃园价邯忻减聂扇孺宰带区笔户偷作计姥低檬
28、牵寞晨泼第9讲动态内存分配736304930第9讲动态内存分配736304930指针与引用的联系引用在底层通过指针来实现一个引用变量,通过存储被引用对象的地址,来标识它所引用的对象引用是对指针的包装,比指针更高级指针是C语言就有的底层概念,使用起来很灵活,但用不好容易出错引用隐藏了指针的“地址”概念,不能直接对地址操作,比指针更安全329.4 深度探索 9.4.1 指针与引用趴讼喀弱羹掉讲沮苛片卢划禾专郎雁遇娜畔枪涎籍劫城树午蜕匀庇锅惨瞄第9讲动态内存分配736304930第9讲动态内存分配736304930引用与指针的选择什么时候用引用?如无需直接对地址进行操作,指针一般都可用引用代替用更多
29、的引用代替指针,更简洁、安全什么时候用指针?引用的功能没有指针强大,有时不得不用指针:引用一经初始化,无法更改被引用对象,如有这种需求,必须用指针;没有空引用,但有空指针,如果空指针有存在的必要,必须用指针;函数指针;用new动态创建的对象或数组,用指针存储其地址最自然;函数调用时,以数组形式传递大量数据时,需要用指针作为参数。339.4 深度探索 9.4.1 指针与引用渡兹束掺尉晋协修咀老狮炙蔼峻恨泛坝遵绒湾洋浊代洒妥莆逮穿迷琳僻瑟第9讲动态内存分配736304930第9讲动态内存分配736304930指针的地址安全性问题地址安全性问题通过指针,访问了不该访问的地址,就会出问题典型问题:数组
30、下标越界问题的严重性:有时会在不知不觉中产生错误,错误源很难定位,因此程序调试起来很麻烦解决方案指针只有赋了初值才能使用(这一点普通变量也应遵循)指针的算术运算,一定要限制在通过指向数组中某个元素的指针,得到指向同一个数组中另一个元素的指针尽量使用封装的数组(如vector),而不直接对指针进行操作349.4 深度探索 9.4.2 指针的安全性隐患及其应对方案处蘸鄂测厌书逞溅锰亨炽叮吏力副堕刽讥傲物尿缮搅胞纺勃宣叭怎押漫眠第9讲动态内存分配736304930第9讲动态内存分配736304930指针的类型安全性问题基本类型数据的转换是基于内容的:例:int i = 2;float x = sta
31、tic_cast(i);目标类型不同的指针间的转换,会使一种类型数据的二进制序列被当作另一种类型的数据:reinterpret_cast:可以将一种类型的指针转换为另一种类型int i = 2;float *p = reinterpret_cast(&i);结论从一种具体类型指针转换为另一种具体类型指针的reinterpret_cast很不安全,一般情况下不要用359.4 深度探索 9.4.2 指针的安全性隐患及其应对方案捏懦附碑巳档插栓姨茵揣趾郁惮孟疽奎蕴许汪谨琅须景归俊弟剖怨伎恩食第9讲动态内存分配736304930第9讲动态内存分配736304930指针的类型安全性问题(续)void指针
32、与具体类型指针的转换:具体类型指针可以隐含地转换为void指针:int i = 2;void *vp = &i;void指针转换为具体类型指针需要用static_cast,例如:int *p = static_cast(vp);但这样就出问题了:float *p2 = static_cast(vp);结论void指针也容易带来不安全,尽量不用,在不得不用的时候,一定要在将void指针转换为具体类型指针时,确保指针被转换为它最初的类型。369.4 深度探索 9.4.2 指针的安全性隐患及其应对方案槽雪写害厅牟手漏劈愉趁胯劳耪萌蜕捍匠趾舰睦话疵温隙喜傍瘁恭柿抒萍第9讲动态内存分配736304930
33、第9讲动态内存分配736304930堆对象的管理堆对象必须用delete删除避免“内存泄漏”原则很简单,但在复杂的程序中,一个堆对象常常要被多个不同的类、模块访问,该在哪里删除,常常引起混乱如何有条理地管理堆对象明确每个堆对象的归属最理想的情况:在一个类的成员函数中创建的堆对象,也在这个类的成员函数中删除把对象的建立和删除变成了一个类的局部问题如需在不同类之间转移堆对象的归属,一定要在注释中注明,作为类的对外接口约定例如在一个函数内创建堆对象并将其返回,则该对象应当由调用该函数的类负责删除379.4 深度探索 9.4.2 指针的安全性隐患及其应对方案龚蕴购倒审歌扯搔瞬喂捎懒崔拄取眷淆预戮狄匆漂
34、塘娶邻晌占抒酥局挤售第9讲动态内存分配736304930第9讲动态内存分配736304930const_cast的应用const_cast的用法用于将常指针、常引用转换为不带“const”的相关类型例:void foo(const int *cp) int *p = const_cast(cp);(*p)+;这可以通过常指针修改指针发对象的值,但这是不安全的用法,因为破坏了接口中const的约定const_cast不是安全的转换,但有时可以安全地使用389.4 深度探索 9.4.3 const_cast的应用那砖假桥捕东追设铲隆倔椅即爵照蛤淫山拘填肮冷翟腿召拿限狠葡烯广料第9讲动态内存分配73
35、6304930第9讲动态内存分配736304930const_cast的安全使用对例6-18的改进例6-18通过下列函数访问数组元素Point &element(int index);问题:由于不是常成员函数,无法使用常对象访问数组元素解决方法:重载element函数:const Point &element(int index) const assert(index = 0 & index size);return pointsindex;新问题代码的重复:这个element函数与原先的函数内容完全相同,两份重复的代码,不易维护399.4 深度探索 9.4.3 const_cast的应用凹工
36、煽涝管转镰鹃鸣至别炒她务腾墙续田坚腺伏诞拾啡怠燃桐卤袱据怯眼第9讲动态内存分配736304930第9讲动态内存分配736304930const_cast的安全使用(续)新问题的解决修改原先的element函数:Point &element(int index) return const_cast( static_cast(this) -element(index);执行过程:调用常成员函数element(),再将其返回结果中的const用const_cast去除将this用static_cast转换为常指针,是安全的转换该函数本身不是常成员函数,确保将最终的结果以普通引用形式返回是安全的思考:
37、如果保留该函数,而修改常成员函数element,使常成员函数element调用该函数,是否合适?409.4 深度探索 9.4.3 const_cast的应用贬灰裂蚂德郸铁叛峡蹄敖棋掠珊集鞭敌滁亦棕旗蹬掌鹿民腋鞋如嫡掺硬摸第9讲动态内存分配736304930第9讲动态内存分配736304930小结419.5 小结主要内容动态存储分配、用vector创建数组对象、深拷贝与浅拷贝。达到的目标理解和掌握动态存储分配技术,会使用vector类,理解对象浅拷贝愈深拷贝的差别,会实现深拷贝。枷猿抑色僵冻侗夕裁咸魂眩恿揖泉兔集菜霉亏哥藻秩曲稀醒愧擞属佳炭亩第9讲动态内存分配736304930第9讲动态内存分配736304930