make_shared与new
创始人
2024-05-06 10:00:09
0

        假设有这么个类:

class A
{private:int b;public:A(int c):b(c) { cout << "call constructor..." << endl;}~A() { cout << "call destructor..." << endl;}int getValue() { return b;}
};

        当创建指向 A 对象的智能指针 shared_ptr 时,很多地方都提到 new 至少执行两次分配,一次用于对象 A 的创建,一次用于智能指针 shared_ptr 的控制块,而如果用 make_shared() 方法通常只需执行一次分配,效率上有所提升。

        这点 Herb Sutter 在他的博客 🔗GotW #89 Solution: Smart Pointers 也有提到:

First, with make_shared the code is simpler. Write for clarity and correctness first.

Second, using make_shared is more efficient. The shared_ptr implementation has to maintain housekeeping information in a control block shared by all shared_ptrs and weak_ptrs referring to a given object...

If you allocate the object separately via a raw new expression, then pass it to a shared_ptr, the shared_ptr implementation has no alternative but to allocate the control block separately...

We’d like to avoid doing two separate allocations here. If you use make_shared to allocate the object and the shared_ptr all in one go, then the implementation can fold them together in a single allocation...

        下面我们从汇编层面看看二者区别,主方法中代码如下:

shared_ptr sn(new A(5));
shared_ptr sm = make_shared(6);

        第一行对应汇编语句如下:

0x0000000100000e64 <+20>:    call   0x100003914
0x0000000100000e69 <+25>:    mov    %rax,%rcx
0x0000000100000e6c <+28>:    mov    %rax,%rdx
0x0000000100000e6f <+31>:    mov    $0x5,%esi
0x0000000100000e74 <+36>:    mov    %rax,%rdi
0x0000000100000e77 <+39>:    mov    %rcx,-0x48(%rbp)
0x0000000100000e7b <+43>:    mov    %rdx,-0x50(%rbp)
0x0000000100000e7f <+47>:    call   0x100000f20 <_ZN1AC1Ei>
0x0000000100000e84 <+52>:    jmp    0x100000e89 
0x0000000100000e89 <+57>:    movl   $0x0,-0x28(%rbp)
0x0000000100000e90 <+64>:    mov    -0x28(%rbp),%edx
0x0000000100000e93 <+67>:    lea    -0x18(%rbp),%rdi
0x0000000100000e97 <+71>:    mov    -0x50(%rbp),%rsi
0x0000000100000e9b <+75>:    call   0x100000f50 <_ZNSt3__110shared_ptrI1AEC1IS1_EEPT_NS_9enable_ifIXsr17__compatible_withIS4_S1_EE5valueENS2_5__natEE4typeE>
0x0000000100000ee3 <+147>:   call   0x10000390e
0x0000000100000ee8 <+152>:   jmp    0x100000f07 
0x0000000100000f07 <+183>:   mov    -0x20(%rbp),%rdi
0x0000000100000f0b <+187>:   call   0x100003878
0x0000000100000f10 <+192>:   ud2

        先是调用了地址在 0x100003914 处的方法,试着通过 info 去看并没查到对应方法名:

(gdb) info symbol 0x100003914
No symbol matches 0x100003914.

        查看代码位置:

lucas@lucasdeMacBook-Pro testCpp % objdump -h testtest:   file format mach-o 64-bit x86-64Sections:
Idx Name             Size     VMA              Type0 __text           00002a27 0000000100000e50 TEXT1 __stubs          000000cc 0000000100003878 TEXT2 __stub_helper    000000f6 0000000100003944 TEXT3 __gcc_except_tab 00000274 0000000100003a3c DATA4 __cstring        0000006b 0000000100003cb0 DATA5 __const          00000102 0000000100003d1b DATA6 __unwind_info    000001d8 0000000100003e20 DATA7 __got            00000068 0000000100004000 DATA8 __const          000000c8 0000000100004068 DATA9 __la_symbol_ptr  00000110 0000000100008000 DATA10 __data           00000008 0000000100008110 DATA

        方法地址位于 __stubs Section__stubs 可理解为动态库链接的桩,具体方法名可在 MachOView 中查看:

        根据地址 0x100003914 可看到对应的 value_Znwm,即 operator new() 方法,在执行几次 mov 指令后还要依次调用如下几个方法。

(gdb) info symbol 0x100000f20
A::A(int) in section .text of /Users/lucas/Documents/study/code/testCpp/test
(gdb) info symbol 0x100000f50
std::__1::shared_ptr::shared_ptr(A*, std::__1::enable_if<__compatible_with::value, std::__1::shared_ptr::__nat>::type) in section .text of /Users/lucas/Documents/study/code/testCpp/test

        最后还要调用值 _ZdlPv 对应的 operator delete() 方法。可以看到通过 new 创建 shared_ptr 对象一共要执行近 20 条汇编指令,而通过 make_shared() 方法呢?

0x0000000100000ea0 <+80>:    movl   $0x6,-0x3c(%rbp)
0x0000000100000ea7 <+87>:    lea    -0x38(%rbp),%rdi
0x0000000100000eab <+91>:    lea    -0x3c(%rbp),%rsi
0x0000000100000eaf <+95>:    call   0x100000f80 <_ZNSt3__1L11make_sharedI1AJiEvEENS_10shared_ptrIT_EEDpOT0_>
0x0000000100000eb4 <+100>:   jmp    0x100000eb9 
0x0000000100000eb9 <+105>:   lea    -0x38(%rbp),%rdi

        无需再调用 operator new()operator delete() 方法,且只需执行一次分配即可,总共也就 6 条指令。

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...