new delete operator new oprator delete operator new[] operator delete[] 详解

来源:互联网 发布:淘宝秋水兰熙旗舰店 编辑:程序博客网 时间:2024/06/05 22:45
在vs2015里,有这么一个文件 vcruntime_new.h
内容大致如下:

#define __CRTDECL __cdecl

    namespace std
    {
        struct nothrow_t { };

        extern nothrow_t const nothrow;
    }
 
 _Ret_notnull_ void* operator new[](
    size_t _Size
    );
 
 _Ret_notnull_ void* operator new(
    size_t _Size
    );
 
   _Ret_maybenull_ void* operator new[](
 size_t                _Size,
    std::nothrow_t const&
    ) throw();
 
 _Ret_maybenull_ void* operator new(
    size_t                _Size,
    std::nothrow_t const&
    ) throw();
 
 void __CRTDECL operator delete(
 void* _Block
    ) throw();

 void __CRTDECL operator delete(
    void* _Block,
    std::nothrow_t const&
    ) throw();

 void __CRTDECL operator delete[](
    void* _Block
    ) throw();

 void __CRTDECL operator delete[](
    void* _Block,
    std::nothrow_t const&
    ) throw();

 void __CRTDECL operator delete(
    void*  _Block,
    size_t _Size
    ) throw();

 void __CRTDECL operator delete[](
    void* _Block,
    size_t _Size
    ) throw();

 #ifndef __PLACEMENT_NEW_INLINE
    #define __PLACEMENT_NEW_INLINE
    _Ret_notnull_
    inline void* __CRTDECL operator new(size_t _Size, void* _Where) throw()
    {
        (void)_Size;
        return _Where;
    }

    inline void __CRTDECL operator delete(void*, void*) throw()
    {
        return;
    }

    _Ret_notnull_
 inline void* __CRTDECL operator new[](size_t _Size,void* _Where) throw()
 {
  (void)_Size;
  return _Where;
 }

    inline void __CRTDECL operator delete[](void*, void*) throw()
    {
    }


_Ret_maybenull_ 告诉你 null   maybe 不知道想表达什么??
_Ret_notnull_   告诉你返回值为非 null
throw()      告诉你这个函数不抛出异常

我们先来分析一下分配内存的函数,这两个函数在分配失败时,抛出bad_alloc异常。
通过调试追踪,两个函数的实现如下:

#define SIZE_MAX 0FFFFFFFFh

void* __CRTDECL operator new[](size_t const size)  //new_array.cpp
{
 return operator new(size);
}

void* __CRTDECL operator new(size_t const size)    //new_scalar.cpp
{
    for (;;)
    {
        if (void* const block = malloc(size))
        {
            return block;
        }

        if (_callnewh(size) == 0)
        {
            if (size == SIZE_MAX)
            {
                __scrt_throw_std_bad_array_new_length();
            }
            else
            {
                __scrt_throw_std_bad_alloc();
            }
        }

        // The new handler was successful; try to allocate again...
    }
}

msdn:
int _callnewh( 
   size_t size 
   ) 
_callnewh 调用当前已经安装的 “new handler” (函数,通过set_new_handle 设置)
参数 size:  new operator 尝试去分配的内存数量
返回值:
  失败,返回0,没有"new handler"安装 或者 没有"new handler"处于活跃状态
  成功,返回1,“new handler"已经安装并处于活跃状态。内存分配可以重试
异常:此函数将抛出 bad_alloc异常,如果不能找到 "new handler"(已经安装)
备注:如果new 运算符无法成功分配内存,"new handle"函数可以启动一些适当的操作。
      如释放内存,以便后续分配成功。
ok,_callnewh和_set_new_handler相关,具体怎样, msdn 有具体的例子,很容易理解。
ok,通过以下代码的调试跟踪:
#include <stdio.h>
#include <new.h>
void main()
{
 void* pVoid = ::operator new(SIZE_MAX);
}
我发现:_callnewh返回了0。所以我们得
 看看vs在void main函数之前是否插入了安装"new handler"函数的代码
#include <stdio.h>
#include <new.h>
void main()
{
 _PNH pnh = _set_new_handler(0);
 printf("%d\n",(int)pnh);
}
输出 0,说明没有安装

ok,这就很好理解了。如果在我们调用 operator new 之前,都没有调用_set_new_handle,
去安装 处理函数(比如释放内存啊)去处理malloc 失败,那么 _callnewh函数将返回0,
接着就是处理 size 是否等于 SIZE_MAX(在这里 SIZE_MAX == 0x0FFFFFFFF),如果是则抛出
bad_array_new_length 异常,否则抛出 bad_alloc异常.

所以 operator new(size_t Size) 分配失败是抛出异常的。

所以下面这样写代码是错误的:
#include <stdio.h>
#include <new.h>
void main()
{
 void* pVoid = ::operator new(SIZE_MAX); //抛出异常
 if(pVoid == nullptr)     //永远也执行不到这里
  printf("opeartor new faile\n"); 
}
#include <stdio.h>
#include <new.h>
void main()
{
 try
 {
  void* pVoid = ::operator new(SIZE_MAX);
 }catch(bad_alloc &ba) //bad_array_new_length是bad_alloc的子类
 {
  printf("%s\n",ba.what());
 }
}

ok ! 顺便贴一下相关的异常类源码,便于以后阅读:


//这两函数是导入函数,从exception,来看,这两个函数是分配内存,和销毁内存,
//因为std::exception 是处理内存无法分配的异常,所以,他的内存分配机制肯定
//不同,源码没有公布出来。
__declspec(dllimport) void __cdecl __std_exception_copy(
    _In_  __std_exception_data const* _From,
    _Out_ __std_exception_data*       _To
    );

__declspec(dllimport) void __cdecl __std_exception_destroy(
    _Inout_ __std_exception_data* _Data
    );
///
/////////////// vcruntime_exception.h start///////////////
struct __std_exception_data
{
    char const* _What;
    bool        _DoFree;
};

namespace std {

 class exception
 {
 public:
  exception() throw()
   : _Data()
  {
  }

  explicit exception(char const* const _Message) throw()
   : _Data()
  {
   __std_exception_data _InitData = { _Message, true };
   __std_exception_copy(&_InitData, &_Data);
  }

  exception(char const* const _Message, int) throw()
   : _Data()
  {
   _Data._What = _Message;
  }

  exception(exception const& _Other) throw()
   : _Data()
  {
   __std_exception_copy(&_Other._Data, &_Data);
  }

  exception& operator=(exception const& _Other) throw()
  {
   if (this == &_Other)
   {
    return *this;
   }
   __std_exception_destroy(&_Data);
   __std_exception_copy(&_Other._Data, &_Data);
   return *this;
  }

  virtual ~exception() throw()
  {
   __std_exception_destroy(&_Data);
  }

  virtual char const* what() const
  {
   return _Data._What ? _Data._What : "Unknown exception";
  }
 private:
  __std_exception_data _Data;
 };

 class bad_exception
  : public exception
 {
 public:
  bad_exception() throw()
   : exception("bad exception", 1)
  {
  }
 };

 class bad_alloc
  : public exception
 {
 public:
    bad_alloc() throw()
        : exception("bad allocation", 1)
    {
    }
 private:
  friend class bad_array_new_length;

  bad_alloc(char const* const _Message) throw()
   : exception(_Message, 1)
  {
  }
 };

 class bad_array_new_length
  : public bad_alloc
 {
 public:

  bad_array_new_length() throw()
   : bad_alloc("bad array new length")
  {
  }
 };
}
/////////////// vcruntime_exception.h end///////////////

ok ,看源码,就很容易理解了。

现在我们来看一下,另外两个能分配内存的函数,这两个函数
分配失败时,返回nullptr ,不抛出异常.

//new_array_nothrow.cpp
void* __CRTDECL operator new[](size_t const size, std::nothrow_t const& x) noexcept
{
    return operator new(size, x);
}

//new_scalar_nothrow.cpp
void* __CRTDECL operator new(size_t const size, std::nothrow_t const&) noexcept
{
    try
    {
        return operator new(size);
    }
    catch (...)
    {
        return nullptr;
    }
}

相当简单,他们最终调用的是 operator new(size_t _Size),
然后捕获 operator new(size_t _Size)异常,
如果 operator new(size_t _Size)分配失败,返回 nullptr;

ok! 现在来分析删除操作的函数
//delete_scalar.cpp
void __CRTDECL operator delete(void* const block) noexcept
{
    #ifdef _DEBUG
    _free_dbg(block, _UNKNOWN_BLOCK);
    #else
    free(block);               //release版本,直接调用 free ,非常简单
    #endif
}

//delete_scalar_size.cpp
void __CRTDECL operator delete(void* block, size_t) noexcept
{
    operator delete(block);
}
 
void __CRTDECL operator delete(
    void* _Block,
    std::nothrow_t const&
    ) throw();  //并未实现,直接调用 opeartor delete(void* block,size_t)

//delete_array.cpp
void __CRTDECL operator delete[](void* block) noexcept
{
    operator delete(block);
}

//delete_array_size.cpp
void __CRTDECL operator delete[](void* block, size_t) noexcept
{
    operator delete[](block);
}

//delete_array_nothrow.cpp
void __CRTDECL operator delete[](void* block, std::nothrow_t const&) noexcept
{
    operator delete[](block);
}

很简单,最终调用的都是free函数,出现那么多 operator delete 的重载函数,主要
是与 operator new 对应。

operator new delete new[] delete[] 是可以重载的。

那就纳闷了, new delete 不是会调用构造函数,虚构函数吗? new X[size] 不是要用 delete[] 来释放吗
楼主,你分析有误吧。怎么没看到构造函数,虚构函数的调用,怎么 delete[] delete 都只是调用了free.
嗯,其实 new delete new X[size] delete[] 是编译器的关键字,只有少部分的人能改变他的操作方式。
我用的是VS,所以只有VS的编写者,能改变。
而  operator new delete new[] delete[] 注意" operator "  ,只负责如何去分配内存。所以我们是可以
通过重载来改变的。

我们通过反汇编来,区分它们的区别
#include <stdio.h>
#include <new.h>

class testClass
{
public:
 testClass(int iValue = 0)
 {
  m_iValue = iValue;
  printf("testClass::testClass()\n");
 }

 ~testClass()
 {
  printf("testClass::~testClass()\n");
 }

 void testFunc()
 {
  printf("testClass::testFunc() m_iValue = %d\n", m_iValue);
 }

private:
 int m_iValue;
};

void main()
{
 testClass* ptc = new testClass(1);
 testClass* ptc1 = (testClass*)::operator new(sizeof(testClass));
 delete ptc; 
 ::operator delete(ptc1);
}    

看一下反汇编代码:
 testClass* ptc = new testClass(1);
//调用 operator new(size_t _Size) 分配内存 
00A91076  push        4 
00A91078  call        operator new (0A910CFh)
//调用 testClass::testClass(int) 构造函数
00A9107D  mov         edi,eax 
00A9107F  push        offset string "testClass::testClass()\n" (0A92190h) 
00A91084  mov         dword ptr [ebp-4],edi 
00A91087  mov         dword ptr [edi],1 
00A9108D  call        printf (0A91040h) 

 testClass* ptc1 = (testClass*)::operator new(sizeof(testClass));
//调用 operator new(size_t _Size) 分配内存 
00A91092  push        4 
00A91094  call        operator new (0A910CFh)
 
 delete ptc;
//调用 testClass::~testClass() 析构函数
00A91099  push        offset string "testClass::~testClass()\n" (0A921A8h) 
00A9109E  mov         esi,eax 
00A910A0  call        printf (0A91040h) 
// 调用 operator delete(void* block,size_t Size) 释放内存
00A910A5  push        4 
00A910A7  push        edi  //edi 为 ptc
00A910A8  call        operator delete (0A9136Ah)
 
 ::operator delete(ptc1);
//调用 operator delete(void* bolck) 释放内存
00A910AD  push        esi  // esi 为ptc1
00A910AE  call        operator delete (0A91378h) 
00A910B3  add         esp,1Ch

operator delete(void* block)  和 operator delete(void* block,size_t size) 最终都调用free 没什么区别
很容易理解。

void main()
{
 testClass* ptc_a = new testClass[6];
 testClass* ptc1_a = (testClass*)::operator new[](sizeof(testClass)*6);
 delete[] ptc_a;
 ::operator delete[](ptc1_a);
}
反汇编:
 testClass* ptc_a = new testClass[6];
//调用 operator new[](size_t size) --> operator new(size_t size) 分配内存
003810A6  push        1Ch   //编译器计算所得内存大小 sizeof(testClass)*6 = 4*6 = 24 = 18h
                            // 18h + 4(保存数组个数 6) = 1ch
003810A8  call        operator new[] (0381506h) 
003810AD  add         esp,4 
//调用testClass::testClass() 构造函数 (循环6次)
003810B0  mov         dword ptr [ebp-10h],eax 
003810B3  mov         dword ptr [ebp-4],0 
003810BA  test        eax,eax 
003810BC  je          main+5Dh (03810DDh) 
003810BE  push        offset testClass::~testClass (0381070h) 
003810C3  push        offset testClass::`default constructor closure' (0381140h) 
003810C8  push        6  //循环6次
003810CA  lea         edi,[eax+4] 
003810CD  mov         dword ptr [eax],6  //保存数组个数
003810D3  push        4 
003810D5  push        edi 
003810D6  call        `eh vector constructor iterator' (038150Fh)
003810DB  jmp         main+5Fh (03810DFh) 
003810DD  xor         edi,edi 

 testClass* ptc1_a = (testClass*)::operator new[](sizeof(testClass)*6);
//调用 operator new[](size_t size) 分配内存 
003810DF  push        18h   //sizeof(testClass)*6 = 4*6 = 24 = 18h
003810E1  mov         dword ptr [ebp-4],0FFFFFFFFh 
003810E8  call        operator new[] (0381506h) 
003810ED  add         esp,4 
003810F0  mov         ebx,eax 

 delete[] ptc_a;
//调用 testClass::~testClass() (循环6次)
003810F2  test        edi,edi 
003810F4  je          main+0A2h (0381122h) 
003810F6  push        offset testClass::~testClass (0381070h) 
003810FB  mov         dword ptr [ebp-4],1 
00381102  lea         esi,[edi-4] 
00381105  push        dword ptr [esi]   //压入数组个数 6
00381107  push        4 
00381109  push        edi 
0038110A  call        `eh vector destructor iterator' (038141Fh) 
//调用 operator delete[](void*,size_t) 释放内存
0038110F  mov         eax,dword ptr [esi] 
00381111  lea         eax,[eax*4+4] 
00381118  push        eax 
00381119  push        esi 
0038111A  call        operator delete[] (0381411h) 
0038111F  add         esp,8
 
 ::operator delete[](ptc1_a);
//调用 operator delete[](void*) 释放内存
00381122  push        ebx 
00381123  call        operator delete[] (038140Ch) 
00381128  add         esp,4 
 
ok,现在明白了吧。new delete 是编译器的关键字
new 先调用 ::operator new 分配内存,然后以分配的内存为 this调用构造函数
delete 先调用 析构函数,再调用 ::operator delete 释放内存
new X[size] 先调用 ::operator new[] 分配内存,在调用每个元素的构造函数
delete[] X  先调用 每个元素的析构函数,再调用 ::operator delete[] 释放内存
前面看过 ::operator new ::operator new[] 源码,其实都是调用 malloc 分配内存
         ::operator delete  ::operator delete[] 都是调用 free 释放内存

ok, 来看一下怎么来重载 这些操作符;

#include <stdio.h>
#include <stdlib.h>
#include <new.h>

class testClass
{
public:
 testClass(int iValue = 0)
 {
  m_iValue = iValue;
  printf("testClass::testClass()\n");
 }

 ~testClass()
 {
  printf("testClass::~testClass()\n");
 }

 void testFunc()
 {
  printf("testClass::testFunc() m_iValue = %d\n", m_iValue);
 }

 void* operator new(size_t Size)
 {
  printf("testClass::operator new(size_t)\n");
  return malloc(Size);
 }

 void operator delete(void* pblock)
 {
  printf("testClass::operator delete(void*)\n");
  if (nullptr != pblock)
   free(pblock);
 }

 void* operator new[](size_t Size)
 {
  printf("testClass::operator new[](size_t)\n");
  return malloc(Size);
 }

 void operator delete[](void* pblock)
 {
  printf("testClass::operator delete[](void*)\n");
  if (nullptr != pblock)
   free(pblock);
 }
private:
 int m_iValue;
};

void main()
{
 testClass* ptc = new testClass(1);
 delete ptc;
 testClass* ptc_a = new testClass[6];
 delete[] ptc_a;
}    

反汇编:
 testClass* ptc = new testClass(1);
//调用重载 operator new(size_t) 分配内存
009010C5  push        offset string "testClass::operator new(size_t)\n"... (090214Ch) 
009010CA  call        printf (0901040h) 
009010CF  mov         edi,dword ptr [__imp__malloc (0902054h)] 
009010D5  push        4 
009010D7  call        edi 
009010D9  mov         esi,eax 
//调用构造函数
009010DB  add         esp,8 
009010DE  mov         dword ptr [ebp-10h],esi 
009010E1  test        esi,esi 
009010E3  je          main+73h (0901113h) 
009010E5  push        offset string "testClass::testClass()\n" (0902118h) 
009010EA  mov         dword ptr [esi],1 
009010F0  call        printf (0901040h) 

 delete ptc;
//调用析构函数
009010F5  push        offset string "testClass::~testClass()\n" (0902130h) 
009010FA  call        printf (0901040h) 
//调用重载 operator delete(void*) 释放内存
009010FF  push        offset string "testClass::operator delete(void*"... (0902170h) 
00901104  call        printf (0901040h) 
00901109  push        esi 
0090110A  call        dword ptr [__imp__free (0902058h)] 
00901110  add         esp,10h 

 testClass* ptc_a = new testClass[6];
//调用重载 operator new[](size_t)分配内存
00901113  push        offset string "testClass::operator new[](size_t"... (0902194h) 
00901118  call        printf (0901040h) 
0090111D  push        1Ch 
0090111F  call        edi 
00901121  add         esp,8 
00901124  mov         dword ptr [ptc],eax 
00901127  mov         dword ptr [ebp-4],0 
0090112E  test        eax,eax 
00901130  je          main+0B1h (0901151h)
//调用每个元素的构造函数 
00901132  push        offset testClass::~testClass (0901070h) 
00901137  push        offset testClass::`default constructor closure' (09011B0h) 
0090113C  push        6 
0090113E  lea         esi,[eax+4] 
00901141  mov         dword ptr [eax],6 
00901147  push        4 
00901149  push        esi 
0090114A  call        `eh vector constructor iterator' (0901530h) 
0090114F  jmp         main+0B3h (0901153h) 
00901151  xor         esi,esi 
00901153  mov         dword ptr [ebp-4],0FFFFFFFFh 

 delete[] ptc_a;
//调用每个元素的析构函数
0090115A  test        esi,esi 
0090115C  je          main+0F2h (0901192h) 
0090115E  push        offset testClass::~testClass (0901070h) 
00901163  mov         dword ptr [ebp-4],1 
0090116A  lea         edi,[esi-4] 
0090116D  push        dword ptr [edi] 
0090116F  push        4 
00901171  push        esi 
00901172  call        `eh vector destructor iterator' (0901449h) 
//调用重载函数 operator delete[](void*) 释放内存
00901177  push        offset string "testClass::operator delete[](voi"... (09021B8h) 
0090117C  call        printf (0901040h) 
00901181  add         esp,4 
00901184  test        edi,edi 
00901186  je          main+0F2h (0901192h) 
00901188  push        edi 
00901189  call        dword ptr [__imp__free (0902058h)] 
0090118F  add         esp,4 
}

new   new X[size]  后者需要为每个元素调用构造函数 ,后者还需要多分配4字节的内存保存数组大小
delete  delete[] X   后者需要为每个元素调用析构函数,delete  delete[] 绝对不是 调用虚构函数的
                     次数的差别,因为以new X[size] 分配的内存的前4个字节是保存数组的大小。所以
      new 分配的用 delete 删除, new X[size] 用 delete[] X 删除, ok !

下面是 placement new delete 的实现,很简单.就不多说了.使用 placement new 需要<new.h>
 #ifndef __PLACEMENT_NEW_INLINE
    #define __PLACEMENT_NEW_INLINE
    _Ret_notnull_
    inline void* __CRTDECL operator new(size_t _Size, void* _Where) throw()
    {
        (void)_Size;
        return _Where;
    }

    inline void __CRTDECL operator delete(void*, void*) throw()
    {
        return;
    }

    _Ret_notnull_
 inline void* __CRTDECL operator new[](size_t _Size,void* _Where) throw()
 {
  (void)_Size;
  return _Where;
 }

    inline void __CRTDECL operator delete[](void*, void*) throw()
    {
    }
 

除了placement new delete new[] delete[]其他的 operator new delete new[] delete[]都可以重载
我还有一篇文章是讲解 placement new 的。
阅读全文
0 0
原创粉丝点击