make_shared与new

文章讨论了在C++中使用`make_shared`与直接使用`new`操作符创建`shared_ptr`的区别。`make_shared`在内存分配上更高效,因为它一次性分配对象和智能指针的控制块,避免了两次内存分配。HerbSutter在其博客中也提到了这一点。文章通过汇编代码展示了`new`操作涉及的多次调用,包括`operatornew`、构造函数、`shared_ptr`构造函数和`operatordelete`,而`make_shared`仅需一次调用即可完成对象和管理结构的创建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        假设有这么个类:

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<A> sn(new A(5));
shared_ptr<A> sm = make_shared<A>(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 <main()+57>
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 <main()+183>
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 test

test:   file format mach-o 64-bit x86-64

Sections:
Idx Name             Size     VMA              Type
  0 __text           00002a27 0000000100000e50 TEXT
  1 __stubs          000000cc 0000000100003878 TEXT
  2 __stub_helper    000000f6 0000000100003944 TEXT
  3 __gcc_except_tab 00000274 0000000100003a3c DATA
  4 __cstring        0000006b 0000000100003cb0 DATA
  5 __const          00000102 0000000100003d1b DATA
  6 __unwind_info    000001d8 0000000100003e20 DATA
  7 __got            00000068 0000000100004000 DATA
  8 __const          000000c8 0000000100004068 DATA
  9 __la_symbol_ptr  00000110 0000000100008000 DATA
 10 __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<A>::shared_ptr<A>(A*, std::__1::enable_if<__compatible_with<A, A>::value, std::__1::shared_ptr<A>::__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 <main()+105>
0x0000000100000eb9 <+105>:   lea    -0x38(%rbp),%rdi

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值