在C语言中,我们写程序时,总是会有动态开辟内存的需求,每到这个时候我们就会想到用malloc/free 去从堆里面动态申请出来一段内存给我们用。但对这一块申请出来的内存,往往还需要我们对它进行稍许的“加工”后即初始化 才能为我们所用,虽然C语言为我们提供了calloc来开辟一段初始化好(0)的一段内存,但面对象中各是各样的数据成员初始化,它同样束手无策。同时,为了保持良好的编程习惯,我们也都应该对申请出来的内存作手动进行初始化。

对此,这常常让我们感到一丝繁琐,于是到了C++中就有了new/delete, new []/delete[] 。用它们便可实现动态的内存管理。

new/delete, new []/delete [] 基本格式


 new/delete动态管理对象,new[]/delete[]动态管理对象数组。

在C++中,把int 、char..等内置类型的变量也看作对象,它们也是存在构造函数和析构函数的,只是通常对它们,系统调用了默认的构造函数来初始化以及默认的析构(编译器优化)。所以new int、new int(3)看起来和普通的定义好像没什么区别。 但对于自定义类型的对象,此种方式在创建对象的同时,还会将对象初始化好;于是new/delete、new []/delete []方式管理内存相对于malloc/free的方式管理的优势就体现出来了,因为它们能保证对象一被创建出来便被初始化,出了作用域便被自动清理。

malloc/free和new/delete的区别和联系


   

  *  malloc/free只是动态分配内存空间/释放空间。而new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理(清理成员)。

  *  它们都是动态管理内存的入口。
  *  malloc/free是C/C++标准库的函数,new/delete是C++操作符
  *  malloc/free需要手动计算类型大小且返回值w为void*,new/delete可自动计算类型的大小,返回对应类型的指针。

  *  malloc/free管理内存失败会返回0,new/delete等的方式管理内存失败会抛出异常。

尽管看起来new、new[] 和malloc 都能开得空间出来,并且以new 、new[]的方式好像还更有优势。但从系统层面看来,真正开出空间来的还是malloc。为什么这么说呢?

在C++ Primer书中有提到说: new/delete的表达式与标准库函数同名了,所以系统并没有重载new或delete表达式。new/delete真正的实现其实是依赖下面这几个内存管理接口的。c++中称之为“placement版”内存管理接口

接口原型:

void * operator new (size_t size);  
void operator delete (size_t size); void * operator new [](size_t size);  
void operator delete[] (size_t size);

探究它,不妨从这样一个类AA开始

 class AA
{
public:
AA(size_t count = )
{
_a = new int[count];
cout<<"AA()"<<endl;
} ~AA()
{
delete[] _a;
cout<<"~AA()"<<endl;
} private:
int* _a;
};

类AA

用AA* pA = new AA[10]创建对象,VS下通过调试进入new表达式内部系统函数,得到下面两个图:

   和

通过上面两个图,大致可以看出来new表达式并不直接开辟内存出来,而是通过调用operator new来获得的内存,而operator new获得的内存实质上还是用malloc开辟出来的。这便证实了前面所述的:开空间出来还是得 malloc来。

同样的道理,delete表达式也不是直接去释放掉内存。比如对上面的对象数组进行delete

AA* pA = new AA[];
delete[] pa;

delete[]实际做了这样几件事情:

  * 依次调用pA指向对象数组中每个对象的析构函数,共10次

  * 调用operator delete[](),它将再调用operator delete

  * 底层用free执行operator delete表达式,依次释放内存

综合相关资料,小结一下operator new/ operator delete:

   1.operator new/operator delete operator new[]/operator delete[] 和 malloc/free用法一样。
   2. 他们只负责分配空间/释放空间,不会调用对象构造函数/析构函数来初始化/清理对象。
   3. 实际operator new和operator delete只是malloc和free的一层封装

如果仔细看过上面的图,可能会有疑惑:new最后将开辟好内存用指针p返回,pA接收它。可为什么p 和pA 会差上4字节?

这其实是因为编译器用相差的这4个字节用来保存一个东西——对象个数,即AA* p = new AA[10] 中的‘10’。这也就不难解释 为什么在delete[] 的时候,不用传给它对象个数。

delete[] 删除时,将new[] 返回的地址再往前移4个字节便可以拿到要析构的对象个数了。

但是注意:new type[] ,只有type显示定义析构函数时,编译器才会多开4字节来保存对象个数。所以像new int、char这样的内置类型编译器不会多开这4字节,编译器自行优化。

它们之间可用下面的图展示:

new/delete, new []/delete[], malloc/free配套使用!


我们new 出来多少个对象,就得调用多少次析构来对它们进行清理。在用new/delete,new[]/delete[], malloc/free进行内存的管理时,一定不能将它们搞混淆,使用它们一定记得配套使用。

来看几个例子,还是以前面AA类为例

 class AA
{
public:
AA(size_t count = )
{
_a = new int[count];
cout<<"AA()"<<endl;
} ~AA()
{
delete[] _a;
cout<<"~AA()"<<endl;
} private:
int* _a;
};

类AA

1.malloc/delete的组合

void Test1()
{ AA* p1 = (AA*)malloc(sizeof(AA)); //没有报错,但不建议采用,容易引起混淆
delete p1;
AA* p2 = (AA*)malloc(sizeof(AA)); //报错,同上,释放位置也不对
delete[] p2;
}

2.delete, delete[] 之间误用(值得注意)

void Test2()
{
AA* p3 = new AA; //不报错,但未清理干净。p3的构造函数开辟的空间没有被释放
free(p3);
AA* p4 = new AA[10]; //崩溃卡死,存在问题,释放位置被后移了4字节。同时只调用了一次析构函数
   delete p4; ,
   AA* p5 = new AA; //报错 非法访问内存
   delete[] p5;
}

①delete p4错误在于释放位置不对(和编译器实现new []的机制有关),导致内存泄漏

②delete[] p5 直接就崩了,这次new AA的时候并未多开4字节保存对象个数,编译器便无法知道要调用多少次析构函数(这里仅仅调用一次析构函数就好了)但编译器内部还是试图去访问p5前4字节的内存,以此获得对象个数;这便非法内存访问了,所以程序就挂了。

3.针对内置类型

void Test3()
{
int* p6 = new int[]; //没问题
delete[] p6;
int* p7 = new int[]; //没问题
delete p7;
int* p8 = new int[]; //没问题
free(p8); }
内存管理内置类型,它们的析构函数其实上是可调可不调的,所以它的实现机制不像前面的new []/delete[],编译器会自行对处理的数据做记录,然后处理;所以即便是不匹配的使用,它们也没出现什么问题。不仅仅这种内置类型如此那种无自定义类型析构函数的类对象,这样的用法同样不会表现出什么问题。但即便如此,为保存良好的编程习惯,还是要配对地使用它们!

结合前面new/delete 的实现机制,便不难分析得出它们若未配对使用可能出现的情况。

总的来说,记住一点即可:new/delete、new[]/delete[] 配套使用总是没错的!

深入理解C++ new/delete, new []/delete[]动态内存管理的更多相关文章

  1. 动态内存管理详解:malloc/free/new/delete/brk/mmap

    c++ 内存获取和释放 new/delete,new[]/delete[] c 内存获取和释放 malloc/free, calloc/realloc 上述8个函数/操作符是c/c++语言里常用来做动 ...

  2. 动态内存管理:malloc/free/new/delete/brk/mmap

    这是我去腾讯面试的时候遇到的一个问题——malloc()是如何申请内存的? c++ 内存获取和释放 new/delete,new[]/delete[] c 内存获取和释放 malloc/free, c ...

  3. C++—动态内存管理之深入探究new和delete

    C++中程序存储空间除栈空间和静态区外,每个程序还拥有一个内存池,这部分内存被称为自由空间(free store)或堆(heap).程序用堆来存储动态分配的对象,即,那些程序运行时分配的对象.动态对象 ...

  4. C++动态内存管理之深入探究new和delete

    C++中程序存储空间除栈空间和静态区外,每个程序还拥有一个内存池,这部分内存被称为自由空间(free store)或堆(heap).程序用堆来存储动态分配的对象,即,那些程序运行时分配的对象.动态对象 ...

  5. 动态内存管理---new&amp;delete

    动态内存管理 动态对象(堆对象)是程序在执行过程中在动态内存中用new运算符创建的对象. 因为是用户自己用new运算符创建的.因此也要求用户自己用delete运算符释放,即用户必须自己管理动态内存. ...

  6. C++动态内存管理之shared_ptr、unique_ptr

    C++中的动态内存管理是通过new和delete两个操作符来完成的.new操作符,为对象分配内存并调用对象所属类的构造函数,返回一个指向该对象的指针.delete调用时,销毁对象,并释放对象所在的内存 ...

  7. (原创)动态内存管理练习 C++ std::vector<int> 模拟实现

    今天看了primer C++的 “动态内存管理类”章节,里面的例子是模拟实现std::vector<std::string>的功能. 照抄之后发现编译不通过,有个库函数调用错误,就参考着自 ...

  8. C++程序设计入门 引用和动态内存管理学习

    引用: 引用就是另一个变量的别名,通过引用所做的读写操作实际上是作用于原变量上. 由于引用是绑定在一个对象上的,所以定义引用的时候必须初始化. 函数参数:引用传递 1.引用可做函数参数,但调用时只需 ...

  9. C++中对C的扩展学习新增语法——动态内存管理

    1.C语言动态内存管理的缺点: 1.malloc对象的大小需要自己计算. 2.需要手动转换指针类型. 3.C++的对象不适合使用malloc和free. 2.C++中new/delete基本使用: 3 ...

  10. C++动态内存管理与源码剖析

    引言 在本篇文章中,我们主要剖析c++中的动态内存管理,包括malloc.new expression.operator new.array new和allocator内存分配方法以及对应的内存释放方 ...

随机推荐

  1. python中的迭代器&&生成器&&装饰器

    迭代器iterator 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束. 迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外, ...

  2. C#读取Excel表格中数据并返回datatable

    在软件开发的过程中,经常用到从excel表格中读取数据作为数据源,以下整理了一个有效的读取excel表格的方法. DataTable GetDataTable(string tableName,str ...

  3. SSH框架之-hibernate 三种状态的转换

    一.遇到的神奇的事情 使用jpa操作数据库,当我使用findAll()方法查处一个List的对象后,给对这个list的实体进行了一些操作,并没有调用update 或者 saveOrUpdate方法,更 ...

  4. 使用定时器限制点击按钮发送短信(附源码)--JavaScript小案例

    不说多哈,有注释哦,直接贴代码了哈,有疑问请追评呢…… 1.禁用按钮: this.disabled = "disabled"(this指按钮)或: this.disabled = ...

  5. 【NOI2004】郁闷的出纳员(splay)

    题面 Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工 作,但是令人郁闷的是,我们的老板反复无 ...

  6. [HNOI2013]消毒

    题目大意: 网址:https://www.luogu.org/problemnew/show/3231 大意:a×b×c的三维空间里有a×b×c个点(x,y,z),其中有些点需要被消除. 消除的方法为 ...

  7. Delphi关于ADO控件的简单使用

    控件:TAdoQuery.ADOConnection.TDataSource.TRzDBGrid 1.新建Vcl Form Application 2.在窗体上添加TADOConnection,设置连 ...

  8. error:com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException

    问题:同样的代码,只能插入一组值,第二组值插入不了 解决:开始我将app_id作为主键,但很明显,同一个app_id会有不同的index,而同一个index也可能对应不同的app_id,因此只能添加一 ...

  9. Linux shell 脚本(二)

    转载请标明出处:  http://blog.csdn.net/zwto1/article/details/45078837:  本文出自:[明月的博客] 五.字符串处理 1.子串截取操作: 路径分割: ...

  10. Android 的自动化测试资源

    环境预备阶段: win7下jdk+eclipse android应用开发环境建立 android genymotion模拟器怎么使用以及和google提供的模拟器性能对比