动态存储分配_第1页
动态存储分配_第2页
动态存储分配_第3页
动态存储分配_第4页
动态存储分配_第5页
已阅读5页,还剩40页未读 继续免费阅读

下载本文档

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

文档简介

动态存储分配第1页,共45页,2023年,2月20日,星期日2023/4/27动态存储分配(难点)程序在内存中的分布new和delete运算符复制构造函数——浅复制和深复制析构函数类的静态成员分为静态数据成员和静态成员函数静态数据成员如何初始化?是通过构造函数吗?静态成员函数的定义和调用规则多文件结构主要内容第2页,共45页,2023年,2月20日,星期日2023/4/27理解复制构造函数的作用及其调用时机,能正确完成含有资源的对象复制理解析构函数的作用和调用时机,能正确利用析构函数完成对象相关资源的清理能正确使用静态成员解决实际问题重点、难点第3页,共45页,2023年,2月20日,星期日2023/4/27回顾--对象的初始化对象在定义的时候对其数据成员进行初始化,称为对象的初始化C++中对象的初始化由一个特殊的成员函数(构造函数)完成其主要功能是创建并初始化对象如果用户不自己定义构造函数,则编译器会自动生成一个默认的构造函数。第4页,共45页,2023年,2月20日,星期日2023/4/27构造函数定义格式:

<类名>::<类名>(<参数表>){<函数体>}构造函数名与类名相同,且不指定函数返回值一般情况下,应该构造函数定义为公有成员编译系统自动生成的默认构造参数其形式为:

<类名>::<类名>(){}第5页,共45页,2023年,2月20日,星期日复制构造函数主要功能:用一个已知的对象来初始化一个同类对象具体声明格式:

<类名>::<类名>(const<类名>&<对象名>)复制构造函数只有一个参数,是该类对象的引用若用户没有显式定义复制构造函数,编译器会自动生成一个默认的复制构造函数,其功能是把已知对象的每个数据成员都复制到新定义的对象中第6页,共45页,2023年,2月20日,星期日对象的清理工作对象的清理工作由析构函数完成主要功能:在对象生存周期即将结束的时刻,由编译系统自动调用来完成一些清理工作在程序块执行完最后一条语句后,系统自动调用析构函数语法格式为:<类名>::~<类名>(){<函数体>}第7页,共45页,2023年,2月20日,星期日计算屏幕上若干点构成的折线长度用Point类描述位于第一象限的点。用户敲击一次ENTER键,程序创建一个点,该点的横纵、坐标值随机,并在[0,100]。敲击5次以后,计算所有点构成的折线总长度。第8页,共45页,2023年,2月20日,星期日分析思路定义Point类,思考该类具有哪些成员定义5个Point类的对象,调用成员函数分别获取每个点的横、纵坐标值通过循环计算5点构成的折线长度难点如何为每个点生成满足题目范围的随机坐标值如何实现用户敲击一次键盘,创建一个点?是否是:Pointpt[5];?????????2023/4/27第9页,共45页,2023年,2月20日,星期日2023/4/27试着思考:为什么定义数组的时候下标必须是常量?intarr[10];为什么不能是:intm,intarr[m];scanf(“%d”,&m);一个已经编译完成的C程序取得并使用4块在逻辑上不同并且用于不同目的的内存区域。程序的内存映象栈堆静态存储区程序区第10页,共45页,2023年,2月20日,星期日2023/4/27程序运行时需要占用多大的空间需要在编译时确定,便于操作系统为程序分配适当的内存空间。从静态存储区分配:程序的全局变量和静态变量都在这里分配,且在编译时已经分配好,在程序终止前被操作系统回收。在栈上创建:在函数调用时,函数的形参和局部变量都在栈上创建,函数执行完毕时这些内存自动被释放掉。从堆(heap)上分配:在程序运行期间,用动态存储分配函数申请和释放的内存程序中变量的内存分配方式第11页,共45页,2023年,2月20日,星期日2023/4/27由动态内存管理系统管理的这块内存区域叫做“堆(heap)”:当需要动态灵活地分配内存空间时,需要用到动态存储分配动态分配得到的内存块位于堆中,且没有名字动态存储分配由库函数malloc.h提供格式:p=(double*)malloc(n*sizeof(double))C++中动态存储分配由运算符new和delete实现new和delete完成动态存储分配时,可以很好地和构造函数、析构函数进行交互,malloc和free不能自行调用构造函数和析构函数动态存储分配第12页,共45页,2023年,2月20日,星期日2023/4/27具体方法:指针变量名=new类型;指针变量名=new类型(初值);指针变量名=new类型[元素个数];当执行new时,有两件事发生:大小适当的空间被分配给特定类型的变量;该存储空间首地址作为new表达式的返回值,若分配失败则返回0;基本类型一般不采用动态存储分配动态存储空间的分配第13页,共45页,2023年,2月20日,星期日2023/4/27用new分配的空间一定要用delete回收具体使用格式:delete指针变量名delete[]指针变量名//用于动态数组的内存回收delete只能用于回收new所分配的空间对于一个指针指向的堆对象只能执行一次delete操作delete[]忽略括符内的具体数字,无论是几维数组的空间回收均只用一个括符表示动态存储空间的回收第14页,共45页,2023年,2月20日,星期日2023/4/27说明以下定义的区别a)intival=1024;c)int*ip2=newint(1024);b)int*pi=&ival;d)int*ip3=newint[1024];第15页,共45页,2023年,2月20日,星期日Point类classPoint{public: Point();//难点 voidprint()const; intgetX()const; intgetY()const;private: intx; inty;};2023/4/27第16页,共45页,2023年,2月20日,星期日//内联函数的声明应出现在函数定义体上inlineintPoint::getX()const{ returnx;}inlineintPoint::getY()const{ returny;}voidPoint::print()const{ cout<<'('<<x<<','<<y<<')'<<endl;}第17页,共45页,2023年,2月20日,星期日Point::Point(){ x=rand()%101; y=rand()%101;}第18页,共45页,2023年,2月20日,星期日intmain(){ Point*ptr[5]={NULL};

srand(time(NULL)); inti=0; for(;cin.get()=='\n'&&i<5;i++) { ptr[i]=newPoint; ptr[i]->print(); }

第19页,共45页,2023年,2月20日,星期日 doubleaver=0; intx,y; cout<<"---------------------------\n"; for(i=0;i<4;i++) { x=ptr[i]->getX()-ptr[i+1]->getX(); y=ptr[i]->getY()-ptr[i+1]->getY(); aver+=sqrt(x*x+y*y); } cout<<"折线总长度是:"<<aver<<endl; for(i=0;i<5;i++) { delete[]ptr[i]; }

system("pause"); return0;}第20页,共45页,2023年,2月20日,星期日2023/4/27练习--设计一个整型的Vector类设计一个动态的整型数组类Vector,它能根据用户提供的参数动态生成元素,即Vectorv1;//生成包含0个元素的空数组Vectorv2(n);//生成包含n个元素的数组Vectorv2(n,m);//生成包含n个元素,每个元素值为m的数组Vector有别于C语言中的静态数组,它有体现数组大小的成员size思考:Vector具有哪些成员?如何在程序运行起来以后,根据用户从键盘输入的n和m创建数组?第21页,共45页,2023年,2月20日,星期日2023/4/27分析Vector的数据成员有:intSIZE;//数组当前元素个数intelems[n];???还是int*elems;???Vector的成员函数有:构造函数(3个,分别实现默认初始化和带参初始化)返回下标为i的元素,intat(intindex);输出数组所有元素,voidprint();返回数组当前元素个数,intsize();动态创建数组需要用到动态存储分配静态数组指数组长度在编译时即定义好,在整个程序运行过程中,大小无法改变动态数组在程序运行中,可根据需要重新指定大小第22页,共45页,2023年,2月20日,星期日2023/4/27vector::vector(intilen,int*ielems){

inti;

length=ilen; elems=newint[length]; for(i=0;i<length;i++) elems[i]=ielems[i];}第23页,共45页,2023年,2月20日,星期日2023/4/27构造函数的初始化列表vector::vector(intilen,int*ielems):length(ilen){

inti;

elems=newint[length]; for(i=0;i<length;i++) elems[i]=ielems[i];}//初始化列表只能出现在成员函数的定义中!!第24页,共45页,2023年,2月20日,星期日2023/4/27#include"vector.h"intmain(){ intarr[5]={1,2,3,4,5};

vectorv1(arr,5),v2; cout<<"elemsatindex"<<3<<":"<<v1.at(3)<<endl; cout<<"lengthofv2is:"<<v2.len()<<endl; return0;}第25页,共45页,2023年,2月20日,星期日2023/4/27vector的析构函数vector::~vector(){cout<<"对象正在析构……"<<endl;delete[]elems;//回收指针elems所指堆内存}第26页,共45页,2023年,2月20日,星期日2023/4/27几点说明析构函数与类同名,无参数,无需指定返回值析构函数不能有参数,因此不能重载析构函数不能回收对象本身所占空间如果不需要做清理工作,类可以没有析构函数思考:在vector中,对象v1和v2能不能正确析构??考虑对象的内存分布……第27页,共45页,2023年,2月20日,星期日2023/4/27对象的复制机制Copy?hello5堆对象s5305053050v1v2Copy123453050第28页,共45页,2023年,2月20日,星期日2023/4/27深复制、浅复制5thello5堆对象s浅复制Copyhello5堆对象shello5堆对象t深复制Copy当对象拥有资源时,C++提供复制构造函数实现深复制第29页,共45页,2023年,2月20日,星期日2023/4/27复制构造函数主要功能:用一个已知的对象来初始化一个同类对象具体声明格式:

<类名>::<类名>(const<类名>&<对象名>)复制构造函数只有一个参数,是该类对象的引用若用户没有显式定义复制构造函数,编译器会自动生成一个默认的复制构造函数,其功能是把已知对象的每个数据成员都复制到新定义的对象中第30页,共45页,2023年,2月20日,星期日2023/4/27vector类的复制构造函数vector::vector(constvector&v){length=v.length;elems=newint[v.length];for(i=0;i<length;i++)elems[i]=v.GetElemsi[i];}第31页,共45页,2023年,2月20日,星期日2023/4/27以下3种情况系统调用复制构造函数:用类的一个对象初始化同类的另一个对象时对象作为函数参数传递给函数形参时函数返回值是类的对象,函数调用完成时返回第32页,共45页,2023年,2月20日,星期日2023/4/27观察下来程序#include<iostream>usingnamespacestd;classCircle{public: Circle(doubleR=1,doublep=3.14){setR(R,p);}

doublegetArea()const{returnpi*r*r;}

doublegetR()const{returnr;}

voidsetR(doubleR=1,doublep=3.14){r=R;pi=p;}private: doubler;

doublepi;};第33页,共45页,2023年,2月20日,星期日2023/4/27intmain(){Circlec1(1),c2(7),c3(6.9); cout<<"areaofc1="<<c1.getArea()<<endl; cout<<"areaofc2="<<c2.getArea()<<endl; cout<<"areaofc3="<<c3.getArea()<<endl; return0;}第34页,共45页,2023年,2月20日,星期日分析Circle类3个对象的内存分布不同对象中存在同一数据的副本浪费储存空间2023/4/2713.1473.146.93.14第35页,共45页,2023年,2月20日,星期日2023/4/27理想状态多个对象共用一个3.14的内存副本13.1476.95.5第36页,共45页,2023年,2月20日,星期日2023/4/27静态数据成员的定义:static<静态数据成员名>;例如:staticintcount;和全局变量一样,静态数据成员在程序一开始就存在,程序结束才销毁,因此必须在程序已开始运行就初始化因此静态数据成员不能在任何函数内分配存储空间和初始化,最好在类的实现部分初始化静态成员的调用:类名::静态成员静态成员第37页,共45页,2023年,2月20日,星期日2023/4/27classCircle{public: Circle(doubleR=1){setR(R);++COUNT;cout<<"obj"<<COUNT<<'('<<r<<')'<<endl;} doublegetArea()const{returnpi*r*r;} doublegetR()const{returnr;} ~Circle(){cout<<"obj"<<COUNT<<"destucting..."<<endl;--COUNT;} voidsetR(doubleR=1){r=R;}

staticintcount(){returnCOUNT;}private: doubler;

staticdoublepi;

staticintCOUNT;};第38页,共45页,2023年,2月20日,星期日doubleCircle::pi=3.14;//静态成员初始化intCircle::COUNT=0;intmain(){Circlec1(1),c2(7),c3(6.9); cout<<"areaofc1="<<c1.getArea()<<endl; cout<<"areaofc2="<<c2.getArea()<<endl; cout<<"areaofc3="<<c3.getArea()<<endl; return0;}2023/4/27第39页,共45页,2023年,2月20日,星期日观察程序运行结果对象的构造函数调用顺序跟析构函数的调用顺序刚好相反构造顺序:c1、c2、c3析构顺序:c3、c2、c1静态成员在这里实现了对Circle类对象进行计数的功能2023/4/27第40页,共45页,2023年,2月20日,星期日2023/4/27小结类的静态数据成员一般在类的实现文件头部进行初始化,避免在主函数的cpp文件初始化类的静态和非静态成员函数均可访问静态数据成员类的静态成员函数能直接访问静态数据成员,若需访问普通数据成员,需将该成员作为函数参数传递进来第41页,共45页,2023年,2月20日,星期日课堂练习--商场的VIP促销活动持VIP卡到商场购物享受9折优惠

温馨提示

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

评论

0/150

提交评论