动态内存管理
来源:互联网 发布:js 浏览器窗口大小 编辑:程序博客网 时间:2024/06/06 13:40
1.C语言动态内存管理
C语言为我们提供标准库函数malloc,realloc,calloc和free进行动态内存管理,我们可以先观察一下这三个函数的函数原型:
void *malloc( size_t size);
void *realloc( void *memblock,size_tsize);
void *calloc( size_t num,size_tsize);
以下简述这三个函数各自的特性:
int main(){ int* p1 =(int*)malloc(sizeof(int)); int* p2 = (int*)calloc(4, sizeof(int)); int* p3 = (int*)realloc(p2,sizeof(int)*6); free(p1); free(p2); free(p3); return 0;}
1.malloc:一个参数(所开辟空间的大小),返回值为void*,需与free配对使用,防止内存泄漏。
2.realloc:可用于第一次开辟空间以及调整动态内存,两个参数(所调整空间的起始位置,所调整空间的大小)。注意:realloc必须接收返回值,因为空间有可能开辟失败,返回为空指针。
3.calloc:两个参数(开辟空间的个数,每一份空间的大小)。
2.C++动态内存管理
我们都知道,C++是在C语言的基础上进行继承和发展的,所以在C++中我们同样可以使用标准库函数malloc,realloc,calloc和free进行动态内存管理。但C++还有更好的方法——new运算符。下面我们试一下这种新技术,若想要开辟一个整型的空间,new/delete运算符将为我们找到一个长度正确的内存块,并返回该内存块的地址。然后用指针来接收这个地址,下面是这样的一个示例:
int* p4 = new int; //开辟1个整型的空间int* p5 = new int(3); //开辟1个整型的空间并初始化为3int* p6 = new int[3]; //开辟3个整型的空间delete p4; //new/delete匹配使用delete p5;delete[] p6; //new[]/delete[]匹配使用
运算符new/delete在使用上比malloc,realloc,calloc方便了很多,但是它们都做了同一件事:动态内存开辟。
我们可以思考一件事:我们知道C++是兼容C的,那么已经有C库malloc/free等来动态管理内存,为什么 C++还要定义new/delete运算符来动态管理内存?
下面我们就深入理解一下这件事:
3.深入理解C++动态内存管理
malloc/free和new/delete的区别和联系?
int* p1 =(int*)malloc(sizeof(int));int* p4 = new int;
上面我们提到new在使用上比malloc方便很多,体现在:malloc需要手动计算类型大小且返回值为void*,使用的时候我们需要将它强转成我们需要的类型;new可自己计算类型大小,并返回对应类型的指针。
上面所述的内容都是在内置类型的基础上,C++是面向对象的语言,我们所使用的往往是自定义类型,下面我们自定义一个类来分析一下malloc和new其他的区别和联系:
class Array{public:Array(size_t size= 10) : _size(size) , _a(0){ cout << "Array(size_t size)" << endl; if (_size> 0) { _a = new int[size]; }}~Array(){ cout << "~Array()"<< endl; if (_a) { delete[]_a; _a = 0; _size = 0; }}private: int* _a; size_t _size;};
这样一个自定义类就创建好了。
我们先看一下malloc:
void Test(){ Array* p1 = (Array*)malloc(sizeof (Array)); free(p1);}
内存开辟成功,再来看下new会有什么不同:
void Test(){ Array* p2 = new Array; delete p2;}
发生了什么?在运算符new动态内存开辟过程中竟然自动调用了构造函数和析构函数。
这个过程是怎样发生的呢?
通过程序调试,我们发现利用new来开辟空间,然后它会自动调用我们的构造函数进行初始化,然后调用析构函数做清理工作,最后delete释放空间。这又是malloc和new的一个大不同。这很好的体现了C++面向对象的语言的特点。
接下来我们调换一下顺序:malloc开出的空间用delete释放、new开出的空间用free释放:
1.
void Test(){ Array* p1 = (Array*)malloc(sizeof (Array)); delete(p1);}
程序并没有什么问题。malloc并不会调用构造函数,delete调用了析构函数。
2.
void Test(){ Array* p2 = new Array; free p2;}
如果用free释放并不会调用析构函数。重点来了,问题就出在这--->new调用了构造函数,在构造函数中已经给p开了空间(p=开空间),但是free并不会调用析构函数,所以p开出的空间没有被释放掉,这就导致了一个很严重的问题:内存泄露。这种情况很危险。
void Test(){ Array* p4 = new Array[3]; delete[] p4;}
new[N]:开辟空间,调用N次构造函数分别初始化每个对象。delete[]调用N次析构函数清理对象,释放空间。
问题又来了,这N次是如何来的?程序中我们并没有指定delete[N].
这个问题我们在下部分内容中会详述。
4.C++的其他内存管理接口
void * operatornew (size_t size);
void operatordelete (void* p);
void * operatornew [](size_t size);
void operatordelete[] (void* p);
标准库函数operator new/operator deletede的命名很容易让人混淆,它并不是运算符重载。实际上我们不能重定义new和delete表达式的行为。
我们可以试验一下:
Array* p5 = (Array*) new(sizeof(Array));
再来看下面一段代码:(我们详细分析一下函数的调用步骤)
void Test1(){ Array* p1 = new Array; delete p1;}
详解如图:
说明一点:对于内置类型而言,没有构造函数、没有析构函数,那么无论调new还是malloc都没有区别;why?调用malloc就相当于直接去开辟空间,new相当于是--->new会调用operator new、operator new里面又去调用malloc。也就是说对于内置类型而言,如果是new出来的空间,调用malloc去释放和调用delete去释放是一样的,最终都会调用free;
但是对于自定义类型就不同了;如果用malloc开辟了空间,free去释放,就不会调用析构函数,没调用析构函数可能就会出现“内存泄露”。
总结一下:
1.operator new/operator delete operator new[]/operator delete[] 和 malloc/free⽤法⼀ 样。
2. 他们只负责分配空间/释放空间,不会调⽤对象构造函数/析构函数来初始化/清理对象。
3. 实际operator new和operator delete只是malloc和free的⼀层封装。
new: 1. 调⽤operator new分配空间;2. 调⽤构造函数初始化对象。
delete: 1. 调⽤析构函数清理对象;2. 调⽤operator delete释放空间。
new[N]: 1. 调⽤operator new分配空间。 2. 调⽤N次构造函数分别初始化每个对象。
delete[]:1. 调⽤N次析构函数清理对象。2. 调⽤operatordelete释放空间。
再回到我们上一小节所提出的问题上:程序中并没有指定delete[N]. 这N次是如何来的呢?
void Test3(){ Array* p4 = new Array[10]; delete[] p4;}
按照我们自定义的类型,这里应该开辟80个字节的空间,为什么是84呢?结合前面的问题猜想一下,多出的这四个字节的空间有可能是用来存放对象个数的,如果真的是这样,那我们上一小节遗留的问题就可以解决了。我们可以验证一下:
为什么要存对象的个数?原因就在于析构要去做一件事:它首先会去调用析构函数,其次会调用operator delete[ ](),调用operator delete[]()的时候根据p的地址在申请空间时多开了4个字节在头上,然后释放的时候也会减去4个字节,传给operator delete,operator delete再传给free。
注意:内置类型(譬如int等)不需要调用构造函数和析构函数。而只有只有调用析构函数才会知道要调用多少次(由对象个数确定),才多开4个字节把对象的个数存下来;对于基本类型直接free就可以,压根不用多开4个字节。
delete[] 析构函数调用细节剖析:
5.定位new表达式
定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。用法如下:
new (place_address)
type new (place_address)
type(initializer-list) place_address必须是⼀个指针,initializer-list是类型的初始化列表。
void Test(){ //1.malloc/free + 定位操作符new()/显⽰调⽤析构函数,模拟 new和delete 的⾏为 Array* p1 = (Array*)malloc(sizeof(Array)); new(p1)Array(100); p1->~Array(); free(p1); //1.malloc/free + 多次调⽤定位操作符new()/显⽰调⽤析构函数,模拟 new[]和delete[] 的⾏为 Array* p2 = (Array*)malloc(sizeof(Array)* 10); for(inti = 0; i < 10; ++i) { new(p2+ i) Array; } for(int i = 0; i < 10; ++i) { p2[i].~Array(); } free(p2);}
- 动态内存管理
- 动态内存管理实例
- C++ 动态内存管理
- 动态内存管理
- 动态内存管理
- 动态内存管理
- 动态内存管理
- 动态内存管理
- 动态内存管理
- 动态内存管理
- 动态内存管理
- 动态内存管理类
- 【007】动态内存管理
- 动态内存管理
- 动态 内存管理
- 动态内存管理
- c++动态内存管理
- 动态内存管理
- SAEA,SoketAsyncEventArgs写的处理函数在高负载时出莫名其妙的错误
- 33 款主宰 2017 iOS 开发的开源库
- HTML课堂讲义(4)
- 我们的文章因他们的存在而变得更有意义
- C++比C多出的一些特殊问题
- 动态内存管理
- 阅读笔记 > 关于代码注释
- windows下配置zoonkeeper
- DFS birthday cake!!!(剪枝与数理分析)
- 安卓中用TCP跨进程通信
- Codeforces Round #421 B. Mister B and Angle in Polygon
- 查找一个字符串中第一个只出现两次的字符
- 【学习笔记】3D图形学:stage3D实战-延迟着色技术DeferredShading
- Struts2(一)