A Defination of Resource: 任何在你的程序中获得并在此后释放的东西。
一、动态内存的使用及内存分配异常的处理
1. new & delete 基础
new&delete属于C++逻辑内存资源中的自由存储区 -- 堆。
C++中所有内存需求都是在程序执行之前通过定义所需要的变量来确定的。当程序的内存需求只能在运行时确定时,即需要的内存取决于用户输入,在这些情况下,程序需要动态分配内存,C++语言将运算符new和delete一起使用进行内存的动态分配管理。由此我们可以总结出
C++动态内存分配的特点:① 通过new关键字进行动态内存申请; ② 动态内存分配是基于类型进行的; ③ delete关键字用于内存释放。
NOTE:程序请求的动态内存从内存堆中分配,但是计算机内存有限,无法保证所有使用operator new分配内存的请求都将由系统授予。
//用于分配类型的元素的块(数组),其中N是这些元素量的整数值
Type *pointer= new Type[N];
//...
delete[] pointer;
//example
int *foo;
foo = new int[5]; //系统为int类型的5个元素动态分配空间,并返回指向序列的第一个元素的指针foo,foo指向一个有效的内存块,其中包含5个int类型元素的空间。
那么问题来了:何时释放?在释放之前和释放之后对数据的操作都有什么要求或者注意事项?
借用深入理解C++ new/delete, new []/delete[]动态内存管理 - tp_16b - 博客园 (cnblogs.com) 一张图。上图清晰的展示出new和delete的作用,接下来我们需要对new和delete对空间的开辟和释放的具体细节做进一步的探究。
2 delete的主要工作
new表达式并不直接开辟内存,而是通过调用operator new来获得内存,而operator new获得的内存实质上还是用malloc开辟出来的。同样,delete表达式也不是直接释放内存,也是借助operator delete的功能。
delete[]的主要工作内容:
① 依次调用 foo指针 指向对象数组中每个对象的析构函数,共5次;
(在C++中,把int, char等 内置类型internal type的变量,每一个数组元素,也看作对象 ,也存在构造、析构函数,通常对它们系统调用了默认地构造函数来初始化以及默认的析构即编译器优化。)
② 调用operator delete[](),再调用operator delete;
③ 底层用free 执行operator delete表达式,依次释放内存。
3 delete的位置
首先,delete[]在删除时将new[]返回的地址向前移动4个字节,得到要析构的对象个数。
new type[],只有type显示定义析构函数时,编译器才会多开4字节来保存对象个数。类似于new int, char这样的内置类型编译器不会多开字节,由编译器自行优化。
//正确用法示例
void Test3(){
int* p6=new int[10];
delete[] p6;
int* p7=new int[10];
delete p7;
int* p8=new int[10];
free(p8);
}
3 new 与 malloc函数的区别
new | malloc |
---|---|
C++中的关键字 | C库提供的函数 |
以具体类型为单位进行内存分配 | 以字节为单位进行内存分配 |
在申请单个变量时可进行初始化 | 不具备内存初始化的特性 |
int *pi = new int(1); //表示动态分配一个int,并初始化值为1
int *pa = new int[1]; //表示动态分配一个数组,数组大小为1
//new关键字的初始化
int *pi = new int(1);
float *pf = new float(2.0f);
char *pc = new char('c');
4 动态分配内存异常机制
对于动态内存分配,C++提供两种标准机制来check分配是否成功。
(1)处理异常。
即在分配失败时抛出bad_alloc类型的异常。正常的分配不成功出现的问题。
(2)nothrow
使用它时出现的情况是,new返回指针为空指针 NULL Pointer, 程序继续正常执行。
5 栈对象
创建栈对象时会移动栈顶指针以挪出适当大小的空间,然后在这个空间上直接调用对应的构造函数以形成一个栈对象,而当函数返回时,会调用其析构函数释放这个对象,然后再调整栈顶指针收回那块栈内存。这个过程中不需要operator new/delete操作,所以将operator new/delete设置为 private不能达到目的。
如果一个类不打算作为基类,通常采用的方案为将其析构函数声明为private。为了限制栈对象,又不限制继承,可以将析构函数声明为protected。
//用这个代码学习一下强制指针类型的强制转换
void main(void){
char* temp = new char[sizeof(NoHashObject)]; //现在ptr是一个指向NoHashObject对象的指针
NoHashObject *obj_ptr=(NoHashObject*)temp;
temp=NULL; //防止通过temp指针修改NoHashObject对象的ptr成员
Resource *rp=(Resource*)obj_ptr; //初始化obj_ptr指向的NoHashObject对象的ptr成员
rp=new Resource();
//...
delete rp; //释放资源
temp = (char*)obj_ptr;
obj_ptr=NULL; //防止产生悬挂指针
delete[] temp; //释放NoHashObject对象所占用的空间
}
【Other references】 C++内存管理(超长,例子很详细,排版很好)_caogenwangbaoqiang的博客-优快云博客_c++ 内存管理