C++幕后故事(十)--对象离咱们而去

三国演义里面说过一句话:天下大事,合久必分,分久必合。有相聚,就有分离的时候。今天咱们主要聊聊operator delete的故事ios

今天咱们主要学习知识点:数组

1.delete的调用流程。 2.咱们重载了delete以后能干啥。 3.placement delete有啥用。函数

1. operator delete操做符的原理

1.1 operator delete 调用流程

测试代码以下:学习

/**************************************************************************** ** ** Copyright (C) 2019 635672377@qq.com ** All rights reserved. ** ****************************************************************************/

/* 测试对象的new、delete,在VS2017更容易观察 */
#ifndef obj_new_delete_h
#define obj_new_delete_h

#include <new>
#include <memory>
#include <iostream>

using std::cout; 
using std::endl;

namespace obj_new_delete 
{

class Obj {
public:
    Obj():mCount(0) { cout << "Obj ctor" << endl; }
    ~Obj() { cout << "~Obj dtor" << endl; }

private:
    int mCount;
};

void test_new_obj() {
    Obj *obj = new Obj();
delete obj;
}
}
#endif // obj_new_delete_h 
复制代码

老规矩,咱们转到反汇编的代码:测试

00287340  mov         eax,dword ptr [obj]  
          ; delete obj
00287343  mov         dword ptr [ebp-104h],eax  
00287349  mov         ecx,dword ptr [ebp-104h]  
0028734F  mov         dword ptr [ebp-0F8h],ecx  
          ; 若是ecx为0,不用调用operator delete和析构函数
00287355  cmp         dword ptr [ebp-0F8h],0  
0028735C  je          obj_new_delete::test_delete_obj+0D3h (0287373h)  
0028735E  push        1  
00287360  mov         ecx,dword ptr [ebp-0F8h]
          ; 析构代理函数
00287366  call        obj_new_delete::Obj::`scalar deleting destructor' (0281212h)  
0028736B  mov         dword ptr [ebp-10Ch],eax  
00287371  jmp         obj_new_delete::test_delete_obj+0DDh (028737Dh)  
00287373  mov         dword ptr [ebp-10Ch],0 

obj_new_delete::Obj::`scalar deleting destructor':
00281212  jmp         obj_new_delete::Obj::`scalar deleting destructor' (0282820h) 

00282840  mov         dword ptr [this],ecx  
00282843  mov         ecx,dword ptr [this]  
          ; 调用对象的析构函数
00282846  call        obj_new_delete::Obj::~Obj (02814C4h)  
0028284B  mov         eax,dword ptr [ebp+8] 
          ; 须要释放内存
0028284E  and         eax,1  
00282851  je          obj_new_delete::Obj::`scalar deleting destructor'+41h (0282861h)  

00282853  push        4  
00282855  mov         eax,dword ptr [this]  
          ; 传递对象的首地址放到eax寄存器中
00282858  push        eax
          ; 调用局部的operator delete,第一参数为首地址,第二个参数为对象的大小
00282859  call        operator delete (0281325h)  
0028285E  add         esp,8  
00282861  mov         eax,dword ptr [this]  

operator delete:
00281325  jmp         operator delete (02832E0h) 

; 调用全局的operator delete,只有一个参数为首地址
operator delete:
002811B3  jmp         operator delete (02840C0h) 
复制代码

这里我顺便把operator delete的源码贴出来ui

// 局部operator delete源码
_CRT_SECURITYCRITICAL_ATTRIBUTE
void __CRTDECL operator delete(void* const block, size_t const) noexcept {
    operator delete(block); 
}

// 全局operator delete源码
_CRT_SECURITYCRITICAL_ATTRIBUTE
void __CRTDECL operator delete(void* const block) noexcept {
    #ifdef _DEBUG
    _free_dbg(block, _UNKNOWN_BLOCK); 
    #else
    free(block);
    #endif
}
复制代码

根据这汇编代码我画出流程图:this

image

看到了这张流程图,你必定对图中○1,○2有几点有疑问。spa

1.什么析构代理函数?scala

从名字中就能够看出就是个代理函数,它从中不只调用咱们本身写的析构函数,还作点其余幕后事情,好比流程图中的是否须要释放内存3d

2.为何在流程图中还有个是否须要释放内存的判断?

你们请看这段代码,是否会释放内存。

void test_delete_obj() {
Obj *obj = new Obj();
obj->~Obj();
}
复制代码

若是咱们本身手动调用了析构函数,这时系统是不会帮咱们释放内存的。

我把上面的代码反汇编看下:

; obj->~Obj()
00EF7340  push        0  
00EF7342  mov         ecx,dword ptr [obj]  
00EF7345  call        obj_new_delete::Obj::`scalar deleting destructor' (0EF1212h) 
复制代码

能够看到这里首先push 0做为一个参数,push 0就表示仅仅调用析构函数,并不会释放内存。仔细看operator delete反汇编代码,这里push 1做为参数,表示须要释放内存,我在上面也作了注释。

  1. 为何NULL指针,能够被delete屡次。

由于C++运行时系统在delete就已经判断了,若是指针为空则不会调用delete。

最后咱们须要注意下:

  1. 咱们代码调用的析构函数,其实不是咱们本身写的析构函数,而是编译器写的析构代理函数。
  2. 析构代理函数里面又作了其余的事,好比是否须要释放内存,再好比对象数组又是怎么释放内存。

1.2 重载delete操做符

只要咱们重载了operator new,就应该对应的重载operator delete,他们两个是一一对应的东西。具体怎么重载的,在《咱们来new个对象》中已经贴出代码了。

2. placement delete

2.1 什么是placement delete?

与placement new是个对应,前者是为了在原有内存上再次构造对象。后者是为了在异常负责回收内存。

2.2 placement delete做用

在通常状况下,其实placement delete起不到做用的。只有在异常状况下,才会被C++运行时系统调用,用来释放内存。

2.3 placement delete重载

具体怎么重载的,就不在多说了,在《咱们来new个对象》中已经贴出代码了。

2.4 一窥系统的placement delete源码

这个源码在vcruntime_new.h中

inline void __CRTDECL operator delete(void*, void*) noexcept {
    return;
}
复制代码

恐怕会让你有点失望,在vs2017的版本中,这个什么里面都没有作的。搞的我也是摸不着头脑。

3.总结

这节咱们知道了operator delete调用流程,对对象的消失有了更深刻的理解。同时知道了析构代理函数存在以及做用。 placement delete做用也是不容咱们忽视的。

image
相关文章
相关标签/搜索