new和delete的深层次剖析(C++)
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++)的更多相关文章
- C++ 中的 delete[] 机制剖析
本文简单总结了delete[]放在析构函数中VS放在主函数中的区别(针对自己定义类). delete原理简单剖析(摘至https://zhidao.baidu.com/question/1540902 ...
- JavaScript 优雅的实现方式包含你可能不知道的知识点
有些东西很好用,但是你未必知道:有些东西你可能用过,但是你未必知道原理. 实现一个目的有多种途径,俗话说,条条大路通罗马.很多内容来自平时的一些收集以及过往博客文章底下的精彩评论,收集整理拓展一波,发 ...
- 深入C++05:运算符重载
运算符重载 1.复数类 运算符重载目的:使对象运算表现得和编译器内置类型一样: 复数类例子 #include<iostream> using namespace std; class CC ...
- 深度剖析malloc、free和new、delete
1.malloc,free是C语言的函数,而new,delete是操作符,属于C++的语法,一定注意这两个不再是函数了,而是操作符. 2.malloc和new对于分配基础类型变量和数组变量,它们除了语 ...
- libevent源码深度剖析
原文地址: http://blog.csdn.net/sparkliang/article/details/4957667 第一章 1,前言 Libevent是一个轻量级的开源高性能网络库,使用者众多 ...
- 计算机程序的思维逻辑 (30) - 剖析StringBuilder
上节介绍了String,提到如果字符串修改操作比较频繁,应该采用StringBuilder和StringBuffer类,这两个类的方法基本是完全一样的,它们的实现代码也几乎一样,唯一的不同就在于,St ...
- C#进阶系列——WebApi 路由机制剖析:你准备好了吗?
前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分. 它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home/Index,那么你只需要配置一个默认路由就能简单搞定: ...
- MapReduce剖析笔记之二:Job提交的过程
上一节以WordCount分析了MapReduce的基本执行流程,但并没有从框架上进行分析,这一部分工作在后续慢慢补充.这一节,先剖析一下作业提交过程. 在分析之前,我们先进行一下粗略的思考,如果要我 ...
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
随机推荐
- 安装Docker到Ubuntu(APT)
运行环境 系统版本:Ubuntu 16.04.5 LTS 软件版本:Docker-CE-18.09.5 硬件配置:无 安装过程 1.卸载旧版本 root@ubuntu:~# sudo apt-get ...
- Oracle中表与包体用户没有操作权限问题
一.表1.在stg用户下查看table_name 表是否存在select * from table_name 2 在表所在用户执行(授权)grant select,update on table_na ...
- centos8 samba
安装dnf install -y samba samba-client开机启动systemctl enable smb立即启动systemctl start smb防火墙放行firewall-cmd ...
- STM32CubeMX自建MDK工程的基本步骤
根据需要调节各总线频率 最下方选项,√去掉,不用实时更新库,选择自己库所在路径就好. 点击左侧, 选择"Code Generator", 选择.c 和 .h文件不分开 最后,点击& ...
- jquery 调用点击事件
触发 click 事件 $(selector).click()
- SpringBoot的应运而生
随着动态语言的流行(Ruby,Groovy,Scala,Node.js),java的开发显得格外的笨重,繁多的配置,低下的效率,复杂的部署流程以及第三方技术集成难度大.springboot应运而生,使 ...
- mysql日期函数及重复数据的查询
-- 日期函数select CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,year(CURRENT_DATE),month(CURRENT_DATE);sel ...
- jQuery---内容复习
内容复习 1. 操作样式 (5) 1.1 css操作 设置单个样式 设置多个样式 获取样式 css(name, value) :设置单个样式 css(obj):设置多个样式 css(name):获取样 ...
- 虚拟化技术xen的简介和安装
虚拟化技术的分类: 1,模拟:Emulation Qemu,PearPC,Bochs 2,完全虚拟化:Full Virtualization,Native Virtualization HVM ...
- 莫凡_linux
1.安装软件 2.基本命令ls和cd cd 指令 第一个要知道的指令就是怎么样去到你想去的地方. cd (Change Directory) 就是干这个的. 找到 Linux 的 terminal 窗 ...