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 条指令。

相关内容

热门资讯

前端-session、jwt 目录:   (1)session (2&#x...
linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
关于测试,我发现了哪些新大陆 关于测试 平常也只是听说过一些关于测试的术语,但并没有使用过测试工具。偶然看到编程老师...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
nodejs:本地安装nvm实... 一、背景-使用不同版本node的原因 vue3+ts、nuxt3版本,node...
JAVA集合知识整理 Java集合知识整理 HashMap相关 HashMap的底层数据结构:jdk1.8之...
无刷直流电机介绍及单片机控制实... 无刷直流电机介绍及单片机控制实例前言基本概念优势与劣势使用寿命基本结构使用单片机控制实例电子调速器&...
fwdiary(2) dp2 1.传纸条  AcWing 275. 传纸条 - AcWing 走两条路,走一条最大的...
常用的DOS命令 常用的DOS命令 DOS(Disk Operating System,磁...
<C++> 类和对象(下) 1.const成员函数将const修饰的“成员函数”称之为const成员函数,cons...