c++中malloc,free,new,delete区别和联系

来源:互联网 发布:网络语大虾是什么意思 编辑:程序博客网 时间:2024/06/07 13:09

  在c/c++中,内存的管理一直是比较复杂的事情,而与内存的分配,释放相关的是malloc,free,new,delete,它们在内存管理中扮演着不同的角色,有相似的地方也有不同的地方,尤其是new操作符,可以被重载,由此产生更复杂的应用,本文将通过实例测试对上述的操作符或函数进行详细的分析!

1.malloc:

        malloc的函数原型是: void *malloc(size_t size),返回一个大小为size字节的连续内存单元。如果想了解malloc的具体工作原理,可以参考我的另一篇博文:http://blog.csdn.net/caoyan_12727/article/details/(题目是:linux Mini CRT堆栈管理的实现解析)。malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用 malloc 函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分 为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连 接表上。调用 free 函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空 闲链上可能没有可以满足用户要求的片段了。于是, malloc 函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲 块合并成较大的内存块。 

2.free

       函数原型是:void free(void *ptr),至于操作系统如何知道要释放内存的大小请参照博客:http://blog.csdn.net/caoyan_12727/article/details/51970919,其实释放的过程就是将这块内存返还给空闲链表。

用malloc分配内存时,操作系统处理会有预分配的性质,即一次性分配一个较大连续空间,防止多次申请内存时不必要的开销(每次申请、释放都耗费资源,不如一次给够),即使全部free掉,操作系统都会留着,以便下次分配,我的一个假设是malloc时,假如申请了1KB的空间,那么操作系统有可能给你一个大于1KB的数值用以备用。同时,malloc时会用一个tag记录本次申请空间的大小,free的时候仅仅将tag清零。相关讨论可参见博客:http://blog.csdn.net/caoyan_12727/article/details/51719011。

3.new

         c++中的new是一个操作符,也是一个关键字,new操作符的行为可以分为3步:

第一步:调用operator new()函数分配内存;分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),如果没有重载,就调用::operator new(size_t ),全局new操作符由C++默认提供.operator new()有如下三种形式
throwing(1)

void* operator new (std::size_t size) throw (std::bad_alloc);
nothrow (2)
void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();
placement (3)
void* operator new (std::size_t size, void* ptr) throw();

(1)(2)的区别仅是是否抛出异常,当分配失败时,前者会抛出bad_alloc异常,后者返回null,不会抛出异常。它们都分配一个固定大小的连续内存。
用法示例:
A* a = new A; //调用throwing(1)
A* a = new(std::nothrow) A; //调用nothrow(2)
(3)是placement new,它也是对operator new的一个重载,定义于<new>中,它多接收一个ptr参数,但它只是简单地返回ptr。

第二步:.在分配的内存上调用placement new来构造对象,我们来看看下面的例子:

#include<stdio.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>#include<iostream>using namespace std;class test{public:int a;int b;test(int x,int y):a(x),b(y){}};int main(){test *p=(test *)::operator new(sizeof(test));//调用第一个版本,申请空间new(p)test(1,2);//调用第3个版本在空间上构造对象cout<<p->a<<p->b<<endl;return 0;}
以上代码经过验证是没有错误的!

第三步:返回对象指针;

4.delete

           delete是对new分配的对象的内存进行释放,返还给操作系统;通常来讲,delete一般用于以下两种情况:

a.释放单个对象:delete ptr, 这种情况比较简单;b.释放对象数组:  delete [] ptr,在释放对象数组时,一定要记得加上[]。在<<深度探索c++对象模型>>中是这样说的:

  寻找数组维度是对delete运算符效率极大的冲击,所以才导致这样的妥协:只有中括号出现时,编译器才去寻找数组的维度,否则它便假设只有单独一个object需要被删除,如果程序员没有提供必要的中括号,那么只有第一个元素的内存空间被释放(或被析构)其他的元素却依然存在,尽管它们的内存被要求归还!!!

至于怎么保存数组的维度,不同的编译器厂商有不同的实现办法,一种可能的解决办法是在对象数组前面额外附加一个4字节的int型对象来存储数组的长度;

0 0
原创粉丝点击