C++中new的源码与行为分析

这是我在处女面中遇到的一个问题,当时回答的不太好,因此下来研究、学习了一下。C++中operator new的行为及其与malloc的区别。当时我回答二者的区别是new在实现中会调用malloc并且由编译器安插调用构造函数的代码,并在面试官前辈的提醒下又答出malloc失败返回0,而new直接抛异常。但不清楚有没有不抛异常的new和不调用构造函数的new这一点。下面结合代码分析一下。

19:     CTest *pTest = new CTest(3, 2);
0027652D 6A 08                push        8  
0027652F E8 8D AD FF FF       call        operator new (02712C1h)  ; 默认的operator new
00276534 83 C4 04             add         esp,4  
00276537 89 85 08 FF FF FF    mov         dword ptr [ebp-0F8h],eax  
0027653D C7 45 FC 00 00 00 00 mov         dword ptr [ebp-4],0  
00276544 83 BD 08 FF FF FF 00 cmp         dword ptr [ebp-0F8h],0  
0027654B 74 17                je          main+74h (0276564h)  
0027654D 6A 02                push        2  
0027654F 6A 03                push        3  
00276551 8B 8D 08 FF FF FF    mov         ecx,dword ptr [ebp-0F8h]  
00276557 E8 B4 AE FF FF       call        CTest::CTest (0271410h)  ; 调用构造函数
0027655C 89 85 E8 FE FF FF    mov         dword ptr [ebp-118h],eax  
00276562 EB 0A                jmp         main+7Eh (027656Eh)  
00276564 C7 85 E8 FE FF FF 00 00 00 00 mov         dword ptr [ebp-118h],0  
0027656E 8B 85 E8 FE FF FF    mov         eax,dword ptr [ebp-118h]  
00276574 89 85 14 FF FF FF    mov         dword ptr [ebp-0ECh],eax  
0027657A C7 45 FC FF FF FF FF mov         dword ptr [ebp-4],0FFFFFFFFh  
00276581 8B 8D 14 FF FF FF    mov         ecx,dword ptr [ebp-0ECh]  
00276587 89 4D EC             mov         dword ptr [pTest],ecx  
    20: 
    21:     CTest *pTest2 = new (std::nothrow) CTest(4, 6);
0027658A 68 D0 C2 27 00       push        offset std::nothrow (027C2D0h)  
0027658F 6A 08                push        8  
00276591 E8 63 AC FF FF       call        operator new (02711F9h)  ;多个参数,不抛异常的operator new
00276596 83 C4 08             add         esp,8  
00276599 89 85 F0 FE FF FF    mov         dword ptr [ebp-110h],eax  
0027659F C7 45 FC 01 00 00 00 mov         dword ptr [ebp-4],1  
002765A6 83 BD F0 FE FF FF 00 cmp         dword ptr [ebp-110h],0  
002765AD 74 17                je          main+0D6h (02765C6h)  
002765AF 6A 06                push        6  
002765B1 6A 04                push        4  
002765B3 8B 8D F0 FE FF FF    mov         ecx,dword ptr [ebp-110h]  
002765B9 E8 52 AE FF FF       call        CTest::CTest (0271410h)  ; 调用构造函数
002765BE 89 85 E8 FE FF FF    mov         dword ptr [ebp-118h],eax  
    20: 
    21:     CTest *pTest2 = new (std::nothrow) CTest(4, 6);
002765C4 EB 0A                jmp         main+0E0h (02765D0h)  
002765C6 C7 85 E8 FE FF FF 00 00 00 00 mov         dword ptr [ebp-118h],0  
002765D0 8B 85 E8 FE FF FF    mov         eax,dword ptr [ebp-118h]  
002765D6 89 85 FC FE FF FF    mov         dword ptr [ebp-104h],eax  
002765DC C7 45 FC FF FF FF FF mov         dword ptr [ebp-4],0FFFFFFFFh  
002765E3 8B 8D FC FE FF FF    mov         ecx,dword ptr [ebp-104h]  
002765E9 89 4D E0             mov         dword ptr [pTest2],ecx  
    22: 
    23:     pTest->m_dw1 = 3;
002765EC 8B 45 EC             mov         eax,dword ptr [pTest]  
002765EF C7 00 03 00 00 00    mov         dword ptr [eax],3  
    24:     pTest2->m_dw2 = 5;
002765F5 8B 45 E0             mov         eax,dword ptr [pTest2]  
002765F8 C7 40 04 05 00 00 00 mov         dword ptr [eax+4],5  
    25:     
    26:     
    27:     return 0;
002765FF 33 C0                xor         eax,eax  
    28: }

operator new的源码:

void* __CRTDECL operator new(size_t const size)
{
    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...
    }
}

operator new的源码,嗯嗯,果然不可能抛异常,因为异常都被接住了:

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

delete操作都是一样的,调用了带两个参数的operator delete,这个应该也是后来搞出来的一个函数,我印象还比较深。之前在尝试将VS2017编译的obj与WDK中lib版CRT库匹配时曾遇到过这个带了两个参数的delete找不到匹配的符号,结果用了/Zc:sizedDealloc-开关才避免其生成。详细过程请见这篇文章

   26:  delete(pTest);
013865FF 8B 45 EC             mov         eax,dword ptr [pTest]  
01386602 89 85 E4 FE FF FF    mov         dword ptr [ebp-11Ch],eax  
01386608 6A 08                push        8  
0138660A 8B 8D E4 FE FF FF    mov         ecx,dword ptr [ebp-11Ch]  
01386610 51                   push        ecx  
01386611 E8 3F AA FF FF       call        operator delete (01381055h)  
01386616 83 C4 08             add         esp,8  
    27:  delete(pTest2);
01386619 8B 45 E0             mov         eax,dword ptr [pTest2]  
0138661C 89 85 D8 FE FF FF    mov         dword ptr [ebp-128h],eax  
01386622 6A 08                push        8  
01386624 8B 8D D8 FE FF FF    mov         ecx,dword ptr [ebp-128h]  
0138662A 51                   push        ecx  
0138662B E8 25 AA FF FF       call        operator delete (01381055h)  
01386630 83 C4 08             add         esp,8  

在其中调用了带一个参数的operator delete:

    28: void __CRTDECL operator delete(void* const block, size_t const) noexcept
    29: {
01384FF0 55                   push        ebp  
01384FF1 8B EC                mov         ebp,esp  
    30:     operator delete(block);
01384FF3 8B 45 08             mov         eax,dword ptr [block]  
    30:     operator delete(block);
01384FF6 50                   push        eax  
01384FF7 E8 36 C0 FF FF       call        operator delete (01381032h)  
01384FFC 83 C4 04             add         esp,4  
    31: }
01384FFF 5D                   pop         ebp  
01385000 C3                   ret  

而这个operator delete则调用了free:

void __CRTDECL operator delete(void* const block) noexcept
{
    #ifdef _DEBUG
    _free_dbg(block, _UNKNOWN_BLOCK);
    #else
    free(block);
    #endif
}

结论:可以通过std::nothrow让new不抛异常,VS17下的做法是在该new函数中用try捕获了异常,并不再抛出,同时让其返回0;对于有构造函数的类对象,new时都会调用其构造函数,是由编译器自动插入代码的。

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
对于C++ STL的源码分析,这是一个广泛而复杂的话题。C++ STL是C++标准库的一部分,包含了许多不同的容器、算法和迭代器等组件,用于提供通用的数据结构和算法支持。 在进行源码分析之前,你需要具备一定的C++编程知识和理解C++模板的工作原理。然后,你可以通过查看STL的源码实现来深入了解其内部机制。 在C++ 11,STL引入了一些新的特性和容器。例如,引用提到的range-based for循环语句,可以更方便地遍历容器的元素。此外,C++ 11还对容器进行了分类,包括序列容器、关联容器和无序容器等。 引用提到了STL的六个主要部分,包括容器、算法、迭代器、函数对象、适配器和分配器。这些部分提供了不同的功能和特性,可以满足各种编程需求。 在C++ 11,一些容器名称发生了变化,如slist被重命名为forward_list,hash_set和hash_map被重命名为unordered_set和unordered_map。这些变化是为了更好地反映容器的功能和语义。 要深入了解STL源码,你可以参考一些重要的资源网站,如cplusplus.com、cppreference.com和gcc.gnu.org。这些网站提供了详细的文档和例子,以帮助你理解STL的实现细节。 总之,要进行C++ STL源码分析,你需要具备一定的编程和模板知识,并参考相关的文档和资源。通过深入研究STL的源码实现,你将能够更好地理解其内部机制和使用方法。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值