c++ 是公司开发最常用的语言之一, 那new和delete 这两个函数是所有开发者即爱又恨的函数。由new 和delete引发的bug , coredump , 让多少程序员加了多少班。

1. 遇到的问题

C++ 中,你也许经常使用 new 和 delete 来动态申请和释放内存,但你可曾想过以下问题呢?

● new 和 delete 是函数吗?

● new [] 和 delete [] 又是什么? 什么时候用它们? 什么情况下等同于new和delete?

● 你知道 operator new 和 operator delete 吗?

● delete是如何知道释放内存的大小?

2. new和delete

1. malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。

2. new 返回指定类型的指针,并且可以自动计算所需要大小; 而 malloc 则必须要由我们计算字节数,并且在返回后强行转换为实际类型的指针。

3. malloc\free只管分配\释放内存,并不能对所得的内存进行初始化\析构,而且获取的内存数值值是随机的。

对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。

4. 既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,结果也会导致程序出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。

3. new[] 和 delete[]

new[]和delete[]的使用看下例代码:

#include <stdio.h>
#include <memory> int iConstructor = ;
int iDestructor = ; class A
{
public:
A()
{
printf("A::Constructor: %d \n", ++iConstructor);
}
~A()
{
printf("A::Destructor: %d \n", ++iDestructor);
}
}; int main()
{
A* pA = new A[]; delete pA; // ✘ pA0~9的内存全部释放 但是只调用了pA[0]的析构 所以若A内还有动态申请内存的话会出现内存泄漏;
//free(pA); // ✘ pA0~9的内存全部释放 pA[0]~p[9]的析构都没调用 所以若A内还有动态申请内存的话会出现内存泄漏;
//delete[]pA; // ✔ 无内存泄漏,正确书写方式,标准规范;
//delete[1]pA; // ✔ 无内存泄漏,书写标准不规范而已;
//delete[10]pA; // ✔ 无内存泄漏,书写标准不规范而已;
//delete[20]pA; // ✔ 无内存泄漏,书写标准不规范而已; {
// std::shared_ptr<A> pAA(new A[10]);
// 等同于 delete pAA;
} return ;
}

 3. operator new 和 operator delete

 4. 内存释放实现机制

delete和free是如何知道应该释放多少内存呢?

如果让我们自己设计的话,我们应该会有这么两种方案:

第一,底层设置一个MemoryManager用来管理申请的内存地址和空间大小;

第二,使用申请内存前的固定区域地址存储有关内存申请信息,类似于报头和报体。

如果用第一种方案的申请、释放内存会涉及插入、查找、删除操作大大影响效率,实际上C++编译器也是用的第二种方案,下面我们就来验证一下。

如上图,pA动态申请1个A的内存空间,地址为0x00766888,内部成员变量iMember占用4个字节,数值为0x64000000(x86小端存储) 即为默认值100。前面16个字节用来管理内存申请的信息,0x04000000为4即为pA的申请内存长度。

那new[]是如何管理的呢?

如上图,pA动态申请20个A的内存空间,地址为0x00779E7C,内部成员变量iMember占用4个字节,数值为0x64000000(x86小端存储) 即为默认值100,总共80个字节,上图0x00779E7C后面连续20个A确实都是100。那我们再看下数组申请是如何定义的呢?

0x54000000为84 为动态申请内存的长度,明明只需要80个字节,为啥多分了4个呢。 我们发现多分了的4个用来存储了数组的个数,这里就是0x14000000。存储数组个数干嘛呢,就是为了遍历调用各个类对象的析构函数额,防止内存泄漏,最后再将这申请的84个字节完全释放掉。方案完美额~ (上两例说是分别申请了4、84个字节,其实堆内存实际占用了20、100个字节额 那16个作为头部用于记录作用啦)。

这里需要注意的是此处的数组个数存储只是针对自定义数据类型才会出现,因为基本数据类型不需要调用析构额,有兴趣的读者可以监视下 int* pArr = new int[20]。

这种思路就是通信协议里报文的报头和报体机制额,报头固定、报体长度可变。

new和delete的深层次剖析(C++)的更多相关文章

  1. C++ 中的 delete[] 机制剖析

    本文简单总结了delete[]放在析构函数中VS放在主函数中的区别(针对自己定义类). delete原理简单剖析(摘至https://zhidao.baidu.com/question/1540902 ...

  2. JavaScript 优雅的实现方式包含你可能不知道的知识点

    有些东西很好用,但是你未必知道:有些东西你可能用过,但是你未必知道原理. 实现一个目的有多种途径,俗话说,条条大路通罗马.很多内容来自平时的一些收集以及过往博客文章底下的精彩评论,收集整理拓展一波,发 ...

  3. 深入C++05:运算符重载

    运算符重载 1.复数类 运算符重载目的:使对象运算表现得和编译器内置类型一样: 复数类例子 #include<iostream> using namespace std; class CC ...

  4. 深度剖析malloc、free和new、delete

    1.malloc,free是C语言的函数,而new,delete是操作符,属于C++的语法,一定注意这两个不再是函数了,而是操作符. 2.malloc和new对于分配基础类型变量和数组变量,它们除了语 ...

  5. libevent源码深度剖析

    原文地址: http://blog.csdn.net/sparkliang/article/details/4957667 第一章 1,前言 Libevent是一个轻量级的开源高性能网络库,使用者众多 ...

  6. 计算机程序的思维逻辑 (30) - 剖析StringBuilder

    上节介绍了String,提到如果字符串修改操作比较频繁,应该采用StringBuilder和StringBuffer类,这两个类的方法基本是完全一样的,它们的实现代码也几乎一样,唯一的不同就在于,St ...

  7. C#进阶系列——WebApi 路由机制剖析:你准备好了吗?

    前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分. 它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home/Index,那么你只需要配置一个默认路由就能简单搞定: ...

  8. MapReduce剖析笔记之二:Job提交的过程

    上一节以WordCount分析了MapReduce的基本执行流程,但并没有从框架上进行分析,这一部分工作在后续慢慢补充.这一节,先剖析一下作业提交过程. 在分析之前,我们先进行一下粗略的思考,如果要我 ...

  9. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

随机推荐

  1. SpringMVC中的参数绑定

    SpringMVC中的参数绑定 参数绑定的定义 所谓参数绑定,简单来说就是客户端发送请求,而请求中包含一些数据,那么这些数据怎么到达 Controller.从客户端请求key/value数据(比如ge ...

  2. 08-SV面向对象编程的高级技巧指南

    1.原始类与扩展类 (1)原始类被称为父类或者超类,扩展类被称为派生类或者子类.扩展类可以直接访问原始类和其本身的所有变量,应该将原始类中的子程序定义成虚拟的,这样它们就可以在扩展类中重定义.new函 ...

  3. 归并排序 ALDS1_5_B:Merge Sort

    Merge Sort Write a program of a Merge Sort algorithm implemented by the following pseudocode. You sh ...

  4. [SDOI2012] Longge的问题 - 欧拉函数

    求 \(\sum\limits_{i=1}^{n}gcd(i,n)\) Solution 化简为 \(\sum\limits_{i|n}^{n}φ(\dfrac{n}{i})i\) 筛出欧拉函数暴力求 ...

  5. php实现自定义中间logo的微信小程序码

    小程序码生成的时候是默认使用小程序后台设置的小程序icon图片的,但是在有些场景我们可能要替换成我们自己想要的icon. 下面先放代码: public function makeNewQrCodeAc ...

  6. Hibernate的save方法不能进行数据库插入

    问题描述 在 MyEcplise 上运行 tomcat,利用 po 模板自动生成 po 文件,调用 po 的 save 方法,不报错,但是无法把数据插入数据库 applicationContext.x ...

  7. Eclipse项目工程导入到IDEA继续开发-超详细

    现在Java开发的主流工具是IDEA,不是说Eclipse,各有各的特色.不过我现在深深的爱上了idea这个工具. 但是之前很多项目都是用eclipse开发的,现在就转入到idea中进行继续开发. 1 ...

  8. 一些常见的HTTP的请求状态码

    200:正确的请求返回正确的结果,如果不想细分正确的请求结果都可以直接返回200. 201:表示资源被正确的创建.比如说,我们 POST 用户名.密码正确创建了一个用户就可以返回 201. 202:请 ...

  9. QT安装和vs2015使用

    下载Qt5.7.0安装包(qt-windows-opensource)与Qt插件(Visual Studio Add-in) QT软件下载地址: http://download.qt.io/archi ...

  10. fastadmin 框架中图片点击放大

    fastadmin的原生图片预览,重新打开一个窗口太麻烦,使用layui做一个弹窗式的图片预览 1.将下面代码放在backend-init.js文件中 $('body').on('click', '[ ...