小结 | C++中new/delete剖析及其宏模拟
来源:互联网 发布:tv霸网络电视下载 编辑:程序博客网 时间:2024/06/01 15:49
1、malloc/free和new/delete
1、C语言中malloc/calloc/realloc
(1)malloc
函数原型:void *malloc( size_t size );
使用范例:int* array_a = (int *)malloc(sizeof(int)*10);//40 byte
说明:malloc动态开辟指定字节数的连续空间,返回void*
需要强转使用,开辟的空间并不初始化。如果开辟失败返回一个NULL指针。
(2)calloc
函数原型:void *calloc( size_t num, size_t size );
使用范例:int* array_b = (int *)calloc(10,sizeof(int));//40 byte
说明:calloc跟malloc的区别,一个是开辟空间大小计算方式不一样,另一个是calloc会初始化空间为0
(3)realloc
函数原型:void *realloc( void *memblock, size_t size );
使用范例:int* array_a = (int *)realloc(array_a, sizeof(int));//40+4 byte
说明:realloc是在已开辟的空间向后增加空间,第二个参数是希望增大的字节数。若内存中有足够大的size的连续空间,那么就在当前空间的后面开辟。但是,不够大,realloc会重新开辟一块空间,再将原来的数据拷贝,同时将原空间释放。如果第一个指针参数是NULL,此时realloc的作用就跟malloc。
(4)free
函数原型:void free( void *memblock );
使用范例:free(array_a);
说明:free函数用于释放动态申请的空间,以避免因动态申请造成的内存泄漏。但不能对同一块空间释放多次,可以对指向NULL的指针进行free。一般free与malloc、calloc、realloc搭配使用。
2、C++中new/delete/new[ ]/delete[ ]
3、C语言的动态开辟与C++的动态开辟
- 两者都是动态开辟的入口
- new会自动计算类型的大小,同时返回指向该空间的同类型指针
- new申请失败会抛异常,malloc等会返回一个NULL指针
- new是一个操作符,malloc等是函数
- 对于自定义类型,new和delete分别会调用构造函数和析构函数
- new在动态申请空间的时候,可以显示的初始化。
2、new/delete、new[]/delete[]剖析
new/delete、new[]/delete[]应该搭配使用,否则有可能出现内存泄漏或者程序奔溃的现象。
1、四个函数
void* operator new(size_t size); //newvoid operator delete(void* ptr); //deletevoid* operator new[](size_t size); //new[]void operator delete[](void* ptr); //delete[]
这四个函数不是 operator 重载,举例:
operator new 是 malloc 的C++封装,相比于malloc,动态申请失败,malloc返回的是NULL,这个函数抛出异常。这是OPP的处理方式。当我们调用new 到时候,系统会调用该函数,然后再调用构造函数。
2举例剖析
(1)一个问题
我们在调用delete[] 的时候,并没有传递个数,但是delete[]会调用N次析构函数,系统是如何知道?
举例如下:
class SeqList{public: SeqList(size_t n = 3) :_a(new int[n]) { cout << "SeqList(size_t n = 3)" << endl; } ~SeqList() { cout << "~SeqList()" << endl; delete[] _a; }private: int* _a;};int main(){ SeqList* s1 = new SeqList[4]; delete[] s1; return 0;}
解析:
这个是因为对于自定义的类型,如果我们显示的调用了析构函数,new[ ]的调用会在开辟的空间之前多开辟一个空间,用于存放个数。当调用delete[ ]的时候,系统会向前读取这个值,从而知道调用几次析构函数。
(2)如果不是自定义类型或者不是显示的定义了析构函数,不会向前开辟多的空间
class AA//显示的调用析构函数{public: AA() {} ~AA() {}private: size_t size;};class BB//未显示的调用了析构函数{public: BB() {}private: size_t size;};//调用int* tmp = new int[10];AA* aa = new AA[10];BB* bb = new BB[10];delete[] bb;delete[] aa;delete[] tmp;
以下几个组合也可以说明这个问题:
//1、AA是显示调用析构函数的自定义函数,会向前开辟一个空间AA* p1 = new AA[10];free(p1); //free不会向前取值,不会调用析构函数。系统奔溃。delete p1; //delete不会向前取值,会调用一次析构函数。系统奔溃
//2、p1 用new开辟空间,不需要向前开辟空间AA* p1 = new AA;delete[] p1;//delete[]会向前索取空间,但是p1没有,系统崩溃
//2、BB没有显示的调用析构函数,不会向前开辟空间。BB* p2 = new BB[10];free(p2); //用free释放没有问题,而且没内存泄漏delete[]p2;//虽然用delete[]会向前索取空间,但是因为BB没有显示调用析构函数,会使用缺省的析构函数,此时编译器优化,正常。
3宏模拟实现new[]/delete[]
(1)模拟new[]
#define NEW_ARRAY(ptr, type, n) \do { \ ptr = (type*)operator new(sizeof(type)*n + 4); \ for (size_t i = 0; i < n; ++i) \ new(ptr + i)type; \} while (); \
new(ptr + i)type;
是一个定位new表达式,他用于对已经分配好内存的指针,进行初始化。一般这个内存的获取是在内存池中。他的格式为new(指针)type
;
下面两者作用一致:
(2)模拟delete[]
#define DELETE_ARRAY(ptr, type) \do { \ size_t n = *((int*)ptr - 1); \ for (size_t i = 0; i < n; ++i) \ (ptr + i)->~AA(); \ operator delete((char*)ptr - 4); \} while ();
注意:使用宏定义多行代码的时候,最好使用换行符,但是换行符后面不能有空格。其次,一定要使用do{…}while(0);
保证宏的一体性。
- 小结 | C++中new/delete剖析及其宏模拟
- 【C++】编程小结① -- new和delete
- 【C++】模拟实现new[]和delete[]
- c++用宏模拟实现new/delete new[]/delete[]
- 【C++】new delete & new[] delete[]
- 模拟实现new、delete和new[] 、delete[]
- C/C++中new/delete malloc/free
- c++中new与delete的用法小结
- C++中operator new 和 new operator小结以及对new 和 delete初步理解
- 深入new和delete小结
- C ++的new、delete
- [C++]new/delete
- C++:new和delete
- new和delete【C++】
- c++new and delete
- C++中new、delete 与new[]、delete[]
- C/C++中new/new[]和delete/delete[]的用法比较
- C++中new、delete
- 空间转换成时间——“改写的广搜”完成深搜(全遍历情况)
- 浅析Java中的final关键字
- ⚔疯狂输出⚔构造方法,构造函数,静态变量的用法
- Maven学习(六)
- 【持久化框架】Mybatis与Hibernate的详细对比--
- 小结 | C++中new/delete剖析及其宏模拟
- 今夜无语..
- 文章标题
- LISP简介
- android 右下角弹出1/4圆盘菜单的效果
- 521. Longest Uncommon Subsequence I
- jQuery时间输入
- 记录一些关于android与unity之间交互的文章
- bzoj 3132 上帝造题的七分钟(二维树状数组区间修改区间查询模板)