版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
动态内存分配1本章主要内容基本数据类型的初始化单个对象内存的动态分配与释放对象数组内存的动态分配与释放使用new和delete在空闲存储区中存储字符串多维数组内存的动态分配与释放内存分配:静态与动态静态内存分配:编译器在处理程序源代码时(即编译时)分配。动态内存分配:程序执行时调用运行时刻库函数来分配。静态与动态内存分配的三个主要区别:静态对象是有名字的变量,我们直接对其进行操作。而动态对象是没有名字的变量,我们通过指针间接地对它进行操作。静态对象的分配与释放由编译器自动处理。程序员需要理解这一点,但不需要做任何事情。相反,动态对象地分配与释放,必须由程序员显示地管理,相对来说比较容易出错。
内存分配内存分配:静态与动态效率与灵活性之间的平衡准则不同。静态内存分配是在程序执行之前进行的因而效率比较高,但是它缺少灵活性,它要求在程序执行之前就知道所需内存的类型和数量。例如:利用静态分配的字符串数组,我们无法很容易地处理和存储任意的文本文件一般来说存储未知数目的元素需要动态内存分配的灵活性。
内存分配概述在C语言中进行内存的动态分配与释放,我们使用malloc()和free()函数。C++中不再使用C语言中的malloc()和free()函数进行内存的动态分配与释放。因为:malloc()函数在运行时从自由内存中分配存储单元。在C++中创建对象时会发生两件事情:(1)为对象分配内存;(2)调用构造函数来初始化那个内存。而构造函数不允许向它传递内存地址来进行初始化。在C++中使用new和delete来进行内存的动态分配与释放。new会触发类对象的构造函数delete会触发类对象的析构函数
动态内存分配动态申请内存操作符new语法格式:类型名T*指针变量名P=
new类型名T(初值列表);功能:在程序执行期间,申请用于存放T类型对象的连续的、未命名的内存空间,并依初值列表赋以初值。结果值:成功:T类型的指针,指向新分配的内存。失败:0(NULL)new运算符是一个一元运算符。它隐含地生成一个函数来调用函数operatornew(),从这个函数返回被分配内存对象的指针。
动态内存分配释放内存操作符delete语法格式:delete指针变量名P;delete[]指针变量名P;功能:释放指针P所指向的内存。P必须是new操作的返回值。delete表达式首先调用析构函数,然后释放内存(经常调用free())。若正在删除的指针是0,则将不发生任何事情,是安全的。故开发人员经常在删除指针后立即把指针赋值为0以免对它删除两次(对一个对象删除两次可能会产生某些问题)。使用deletevoid*可能会出错如果想对一个void*类型指针进行delete操作,要注意这将可能成为一个程序错误,除非指针所指的内容是非常简单的,因为它将不执行析构函数。一个void*指针被初始化时指向“元”数据(它没有指向含有析构函数的对象),在调用delete删除void*指针时不会发生什么错误,因为所需要的仅是释放指针本身(对象)的这块内存。但这样只会释放对象的内存,不调用析构函数,从而也就不会释放指针所指向的数据的内存,这样就会引起内存泄漏。编译这样的程序时,编译器会认为我们知道所做的一切,从而编译器不会给出任何警告。所以对于void指针必须转换为适当的类型。如何为单个对象分配动态内存语法格式:
数据类型T*指针变量名P=new数据类型T(初始化列表);示例:
int*ptrInt=newint;//Oneint
double*ptrDouble=newdouble;//Onedoublefloat**ptrPtrFloat=newfloat*;//Onepointer-to-float不同类型所占用的不同的空间由编译器自动处理。
动态内存分配如何初始化基本类型C++中很重要的一点就是遵循PITA规则,即要在创建对象时初始化对象而不是先创建对象然后再赋值。可以在任何时候初始化由空闲存储区分配的单个对象。初始化方法:在特定的类型后面使用圆括号内的初始化值来完成。
new数据类型T(初始化列表);
动态内存分配示例:
int*ptrInt=newint(100);double*ptrDouble=newdouble(1.414);float*ptrFloat=newfloat(3.1415926F);float**ptrPtrFloat=newfloat*(ptrFloat);
动态内存分配如何初始化基本类型空圆括号的含义在new表达式中,可以在类型名后使用空圆括号:new数据类型T();功能:将初始化为数据类型T的默认值。
动态内存分配new数据类型T;与new数据类型T();的区别示例:
int*ptrInt1=newint;//Valueisunknown
int*ptrInt1=newint();//Valueiszero说明:第一种情况下,在空闲存储区中创建了一个int类型对象,但其值未知。在第二种情况下,在空闲存储区中创建了一个int类型对象并初始化为int在全局作用域中的值(默认值)。而所有的全局基本类型在默认情况下的初始值为0,故第二种情况在创建了对象后将其值初始化为0。
动态内存分配如何释放单个对象的空闲空间语法格式:delete指针变量名P;指针变量名P=NULL;功能:释放指针P所指向的内存。P必须是new操作的返回值。删除基于0的指针(空指针)总是一种安全的操作。
动态内存分配如何为对象数组分配空闲空间语法格式:数据类型T*指针变量名P=new数据类型T[元素个数];功能:在空闲空间分配对象数组,指针P指向数组的第一个元素。如果在方括号中的表达式的值为0,则就会分配没有元素的数组,new表达式返回的指针是非0值,并且与其他指向任何对象的指针都不同。如果这个值在运行期间被确定是无效的,则结果不可预知。
动态内存分配如何为对象数组分配空闲空间示例:
//Arrayof5intsint*ptrInt=newint[5];
//Arrayof6doubles
intdimension=6;double*ptrDouble=newdouble[dimension];
动态内存分配如何为对象数组分配空闲空间不可能初始化数组:在C++中并没有语法来真正的初始化空闲存储区中的各个元素(不遵循PITA规则)。对于所有的基本数据类型而言,每个元素的值都是未知的,所能做的就是在创建数组之后,再给每个元素赋值,如下所示:
intconstdimension=5;
//Allocatespacefor5ints,alluninitializedint*ptrInt=newint[dimension];
//Providesomemeaningfulvaluesfor(inti=0;i<dimension;++i)
ptrInt[i]=i;说明:空闲存储区中用户自定义数据类型的数组可以被初始化。
动态内存分配如何释放对象数组的空闲空间语法格式:delete[]指针变量名P;功能:释放空闲存储区中的对象数组内存空间。说明:必须用方括号“[]”来通知编译器这是一个对象数组,该代码的任务是将从数组创建时存放在某处的对象数量取回并为数组的所有对象调用析构函数。当删除用户自定义数据类型的数组时非常重要。同释放单个对象一样,针对包含0值的指针使用delete被认为是安全的。
动态内存分配保持平衡涉及到new以及delete的主要规则相当简单:使用delete来平衡每一个new。如果在new语句中使用了方括号(即分配了对象数组),必须在delete后使用方括号。无论何时使用new,一定要确保使用正确的delete格式。
动态内存分配如何分配和删除多维数组可以为任意维数的数组分配空间,并非仅能为一维数组分配空间。示例:
introws=3;
intconstcols=5;
double(*ptr)[cols]=newdouble[rows][cols];
//……delete[]ptr;
动态内存分配如何分配和删除多维数组示例说明:二维数组的元素是一维数组的集合(行)。故为了创建一个指向一维数组的指针,包含*ptr的圆括号是必须的。ptr+1代表了第二个一维数组的开始(第一行,第0列),而不是第二个double元素(第0行,第一列)。当使用new从空闲存储区中给数组分配空间时,所有的维数(除了第一维)都必须让编译器知道并且必须是正数。这就是rows可以在运行期间确定,而cols必须是常量从而让编译器知道的原因。如何分配和删除多维数组当删除数组时,注意delete语句的格式与一维数组的格式相同。即列的数目不需要指定。使用typedef可以简化语法。如:
introws=3;
intconstcols=5;
typedefdoubleONE_DIM[cols];
ONE_DIM*ptr=newONE_DIM[rows];
//……delete[]ptr;命名该空间当使用new在空闲存储区中为一个对象分配空间时,会得到一个指向对象的指针,然后解引用这个指针来访问该对象。但是也可以不使用解引用的指针来访问空闲存储区中的对象,为此,只需要解引用有new返回的地址并创建别名,然后就可以简单地使用这个别名来引用这个对象。不过因为delete需要所分配的空闲存储区的地址,故注意要小心使用取地址运算符&。
double&refFreeStore=*newdouble(3.1416);
refFreeStore=3.1415926;//changethecontent
//……
delete&refFreeStore;
//releasethespace两个程序输出示例intmain(){ int*ptrFreeStore=newint(1);
int&refFreeStore=*ptrFreeStore; cout<<refFreeStore<<endl; ptrFreeStore=newint(2); cout<<refFreeStore<<endl; delete&refFreeStore; deleteptrFreeStore;}intmain(){ int*ptrFreeStore=newint(1);
int*&refPtrFreeStore=ptrFreeStore; cout<<*refPtrFreeStore<<endl; deleterefPtrFreeStore; ptrFreeStore=newint(2); cout<<*refPtrFreeStore<<endl; deleterefPtrFreeStore;}运行结果:11运行结果:12例1动态创建对象举例usingnamespacestd;classPoint{public:Point(){X=Y=0;cout<<"DefaultConstructorcalled.\n";}Point(intxx,intyy){X=xx;Y=yy;cout<<"Constructorcalled.\n";}~Point(){cout<<"Destructorcalled.\n";}intGetX(){returnX;}intGetY(){returnY;} voidMove(intx,inty) {X=x;Y=y;}private:intX,Y;};intmain(){cout<<"StepOne:“<<endl;Point*Ptr1=newPoint;deletePtr1;cout<<"StepTwo:“<<endl;Ptr1=newPoint(1,2);deletePtr1;}运行结果:StepOne:DefaultConstructorcalled.Destructorcalled.StepTwo:Constructorcalled.Destructorcalled.26例2动态创建对象数组举例usingnamespacestd;classPoint{
//类的声明同例1,略};intmain(){Point*Ptr=newPoint[2];//创建对象数组
Ptr[0].Move(5,10);//通过指针访问数组元素的成员
Ptr[1].Move(15,20);//通过指针访问数组元素的成员
cout<<"Deleting...“<<endl;delete[]Ptr;//删除整个对象数组}运行结果:DefaultConstructorcalled.DefaultConstructorcalled.Deleting...Destructorcalled.Destructorcalled.28例3动态数组类usingnamespacestd;classPoint{
//类的声明同例1…
};classArrayOfPoints{public:ArrayOfPoints(intn){numberOfPoints=n;points=newPoint[n];}~ArrayOfPoints(){cout<<"Deleting...“<<endl;numberOfPoints=0;delete[]points;}Point&Element(intn){returnpoints[n];}private:Point*points;intnumberOfPoints;};29intmain(){ intnumber; cout<<"Pleaseenterthenumberofpoints:"; cin>>number;
//创建对象数组
ArrayOfPointspoints(number);
//通过指针访问数组元素的成员
points.Element(0).Move(5,10);
//通过指针访问数组元素的成员
points.Element(1).Move(15,20);}30运行结果如下:Pleaseenterthenumberofpoints:2DefaultConstructorcalled.DefaultConstructorcalled.Deleting...Destructorcalled.Destructorcalled.31动态创建多维数组new类型名T[下标表达式1][下标表达式2]…;如果内存申请成功,new运算返回一个指向新分配内存首地址的指针,是一个T类型的数组,数组元素的个数为除最左边一维外各维下标表达式的乘积。例如:char(*fp)[3];fp=newchar[2][3];char(*fp)[3];fpfp+1fp[0][0]fp[0][1]fp[0][2]fp[1][0]fp[1][1]fp[1][2]33浅拷贝与深拷贝浅拷贝实现对象间数据元素的一一对应复制。深拷贝当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指的对象进行复制。浅拷贝与深拷贝例5对象的浅拷贝usingnamespacestd;classPoint{
//类的声明同例1//……};classArrayOfPoints{
//类的声明同例3//……};浅拷贝与深拷贝intmain(){ intnumber; cin>>number;ArrayOfPointspointsArray1(number);pointsArray1.Element(0).Move(5,10);pointsArray1.Element(1).Move(15,20);
ArrayOfPointspointsArray2(pointsArray1);
cout<<"CopyofpointsArray1:“<<endl;cout<<"Point_0ofarray2:"<<pointsArray2.Element(0).GetX()<<",“<<pointsArray2.Element(0).GetY()<<endl;cout<<"Point_1ofarray2:"<<pointsArray2.Element(1).GetX()<<",“<<pointsArray2.Element(1).GetY()<<endl;36pointsArray1.Element(0).Move(25,30);pointsArray1.Element(1).Move(35,40);cout<<"AfterthemovingofpointsArray1:“<<endl;cout<<"Point_0ofarray2:"<<pointsArray2.Element(0).GetX()<<","<<pointsArray2.Element(0).GetY()<<endl;cout<<"Point_1ofarray2:"<<pointsArray2.Element(1).GetX()<<",“<<pointsArray2.Element(1).GetY()<<endl;}37运行结果如下:Pleaseenterthenumberofpoints:2DefaultConstructorcalled.DefaultConstructorcalled.CopyofpointsArray1:Point_0ofarray2:5,10Point_1ofarray2:15,20AfterthemovingofpointsArray1:Point_0ofarray2:25,30Point_1ofarray2:35,40Deleting...Destructorcalled.Destructorcalled.Deleting...接下来程序出现异常,也就是运行错误。38拷贝前拷贝后pointsArray1的数组元素占用的内存pointsnumberOfPointspointsArray1pointsnumberOfPointspointsArray1pointsArray1的数组元素占用的内存pointsnumberOfPointspointsArray239浅拷贝例6对象的深拷贝usingnamespacestd;classPoint{
//类的声明同例1};classArrayOfPoints{public:ArrayOfPoints(ArrayOfPoints&pointsArray);
//其它成员同例3};浅拷贝与深拷贝ArrayOfPoints::ArrayOfPoints(ArrayOfPoints&pointsArray){numberOfPoints=pointsArray.numberOfPoints;points=newPoint[numberOfPoints];for(inti=0;i<numberOfPoints;i++)
points[i].Move(
pointsArray.Element(i).GetX(),
pointsArray.Element(i).GetY());}intmain(){
//同例5}41程序的运行结果如下:Pleaseenterthenumberofpoints:2DefaultConstructorcalled.DefaultConstructorcalled.DefaultConstructorcalled.DefaultConstructorcalled.CopyofpointsArray1:Point_0ofarray2:5,10Point_1ofarray2:15,20AfterthemovingofpointsArray1:Point_0ofarray2:5,10Point_1ofarray2:15,20Deleting...Destructorcalled.Destructorcalled.Deleting...Destructorcalled.Destructorcalled.42拷贝前pointsArray1的数组元素占用的内存pointsnumberOfPointspointsArray1拷贝后pointsnumberOfPointspointsArray1pointsArray1的数组元素占用的内存pointsnumberOfPointspointsArray243深拷贝*44/106对象成员的另一种访问方式声明形式类名*对象指针名;例:PointA(5,10);Piont*ptr=NULL;ptr=&A;通过指针访问对象成员对象指针名->成员名ptr->getx()相当于(*ptr).getx();
指针44*45/106曾经出现过的错误例子classFred;//前向引用声明classBarney{Fredx;//错误:类Fred的声明尚不完善
};classFred{Barneyy;};classFred; //前向引用声明classBarney{Fred*x; };classFred{Barneyy;};45指向类的非静态成员的指针申明通过指向成员的指针只能访问公有成员声明指向成员的指针声明指向公有数据成员的指针类型说明符类名::*指针名;
声明指向公有函数成员的指针类型说明符(类名::*指针名)(参数表);46类的非静态成员的指针使用指向数据成员的指针说明指针应该指向哪个成员指针名=&类名::数据成员名;通过对象名(或对象指针)与成员指针结合来访问数据成员对象名.*类成员指针名或:对象指针名—>*类成员指针名47类的非静态成员的函数指针使用指向函数成员的指针初始化指针名=类名::函数成员名;通过对象名(或对象指针)与成员指针结合来访问函数成员(对象名.*类成员指针名)(参数表);或:(对象指针名—>*类成员指针名)(参数表);48指向类的非静态成员的指针例13访问对象的公有成员函数的不同方式intmain(){PointA(4,5);//声明对象A Point*p1=&A;//声明对象指针并初始化
//声明成员函数指针并初始化
int(Point::*p_GetX)()=Point::GetX;
//(1)使用成员函数指针访问成员函数
cout<<(A.*p_GetX)()<<endl;
//(2)使用对象指针访问成员函数
cout<<(p1->GetX)()<<endl;
//(3)使用对象名访问成员函数
cout<<A.GetX()<<endl; }49例15通过指针访问类的静态函数成员#include<iostream>usingnamespacestd;classPoint//Point类声明{public://外部接口(公有数据成员)
//其它函数略
staticvoidGetC()//静态函数成员
{cout<<"Objectid="<<countP<<endl;}private://私有数据成员
intX,Y; staticintcountP; //静态数据成员引用性说明};//函数实现略intPoint::countP=0; //静态数据成员定义性说明50intmain(){
//指向函数的指针,指向类的静态成员函数
void(*gc)()=Point::GetC; PointA(4,5); //声明对象A cout<<"PointA,"<<A.GetX()<<","<<A.GetY(); gc();//输出对象序号,通过指针访问静态函数成员
PointB(A); //声明对象B cout<<"PointB,"<<B.GetX()<<","<<B.GetY(); gc();//输出对象序号,通过指针访问静态函数成员}51引用的概念引用:
为一个变量、函数等对象规定一个别名,该别名称为引用。此后,对别名的操作即是对别名所代表的对象的操作。即引用变量创建了变量和对象的别名。引用虽是一种类型,但不是值,只能用它标识另一个对象。从理论意义上说,引用是一种映射,把一个标识符映射到一个对象。从直观意义上说,引用是用一个标识符给一个对象起了一个别名,引用标识对象,就是用一个别名标识对象。52引用的创建(声明)创建(声明)引用:创建引用与定义指针类似,只不过将*换成&。引用只有声明,没有定义。因为引用不占存储空间,而定义要分配存储空间。创建(声明)引用格式如下:
类型&别名[=别名所代表的对象];例:
inti=0;
int
&ir=i;//定义引用ir作为对象i的别名
ir=2;//形式上向ir赋值,实际上是向i赋值,等同于i=2;
int*p=&ir;//形式上取ir的地址,实际上是取i的地
//址,等同于int*p=&i;
53引用的初始化引用若不是作为函数参数的,则必须初始化(PITA),并且必须引用某个“真实”的变量或对象。否则发生编译错误。方法:一般使用相同类型的变量来初始化。例:intsomeVar=1;
int&ref=someVar;引用总是作为目标的别名使用。也就是说:引用一旦初始化,它就维系在所代表的对象上,再也分不开。任何对该引用的赋值,都是对引用所维系目标赋值,而不是将引用维系到另一目标上。即一旦引用变量被声明并初始化,在引用的作用域内,不能再作为其他对象的引用(别名)。54创建对数组的引用可以创建对数组的引用。例:intconstdimension=5;doublearray1[dimension]={0.0};double(&refArray1)[dimension]=array1;//……doubleconstarray2[dimension]={0.0
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 南京工业大学浦江学院《应用统计学》2022-2023学年第一学期期末试卷
- 南京工业大学浦江学院《社会统计学》2023-2024学年第一学期期末试卷
- 分数的基本性质说课稿
- 蹲踞式跳远说课教学反思
- 住宅楼长螺旋钻孔CFG灌注桩基础工程施工方案
- 《月是故乡明》说课稿
- 南京工业大学浦江学院《合同管理》2023-2024学年第一学期期末试卷
- 南京工业大学浦江学院《服务设计》2021-2022学年第一学期期末试卷
- 终止合作协议书(2篇)
- 提高4-5岁幼儿自我控制能力的教育策略
- 2024-2030年狂犬疫苗行业市场深度分析及发展策略研究报告
- 《基因指导蛋白质的合成》(第 1课时)教学设计
- 2024-2030年果蔬行业市场发展现状及竞争格局与投资战略研究报告
- 2 0 2 4 年 7 月 国开专科《法理学》期末纸质考试 试题及答案
- 公共政策分析第一章
- 行业协会重大活动备案报告制度
- 北京市海淀区2024学年七年级上学期语文期中试卷【含参考答案】
- 2024年新人教版七年级上册数学教学课件 5.2 解一元一次方程 第4课时 利用去分母解一元一次方程
- Unit 4 My Favourite Subject教学设计2024-2025学年人教版(2024)英语七年级上册
- 2024新信息科技三年级第四单元:创作数字作品大单元整体教学设计
- 第9课《这些是大家的》(课件)-部编版道德与法治二年级上册
评论
0/150
提交评论