new [], delete [] complexity

后端 未结 4 1393
眼角桃花
眼角桃花 2021-02-07 10:23

I already know that the new[] operator first allocates memory and then calls the constructor for each element and that the delete[] operator first call

相关标签:
4条回答
  • 2021-02-07 10:36

    When you don't know, it's great idea to use assembly output. For example, let's assume this is the code to compare.

    class foo
    {
    public:
        int a;
        foo()
        {
            a = 0;
            // more stuff
        }
        ~foo()
        {
            a = 1;
            // some useful stuff here
        }
    };
    
    class boo
    {
    public:
        int a;
    };
    
    void remove_foo(foo* pfoo) {
        delete [] pfoo;
    }
    
    void remove_boo(boo *pboo) {
        delete [] pboo;
    }
    

    When compiling with optimizations using gcc (Clang gives similar output), you get the following result.

        .file   "deleter.cpp"
        .text
        .p2align 4,,15
        .globl  _Z10remove_fooP3foo
        .type   _Z10remove_fooP3foo, @function
    _Z10remove_fooP3foo:
    .LFB6:
        .cfi_startproc
        testq   %rdi, %rdi
        je  .L1
        movq    -8(%rdi), %rax
        leaq    (%rdi,%rax,4), %rax
        cmpq    %rax, %rdi
        je  .L4
        .p2align 4,,10
        .p2align 3
    .L6:
        subq    $4, %rax
        movl    $1, (%rax)
        cmpq    %rax, %rdi
        jne .L6
    .L4:
        subq    $8, %rdi
        jmp _ZdaPv
        .p2align 4,,10
        .p2align 3
    .L1:
        rep ret
        .cfi_endproc
    .LFE6:
        .size   _Z10remove_fooP3foo, .-_Z10remove_fooP3foo
        .p2align 4,,15
        .globl  _Z10remove_booP3boo
        .type   _Z10remove_booP3boo, @function
    _Z10remove_booP3boo:
    .LFB7:
        .cfi_startproc
        testq   %rdi, %rdi
        je  .L8
        jmp _ZdaPv
        .p2align 4,,10
        .p2align 3
    .L8:
        rep ret
        .cfi_endproc
    .LFE7:
        .size   _Z10remove_booP3boo, .-_Z10remove_booP3boo
        .ident  "GCC: (SUSE Linux) 4.8.1 20130909 [gcc-4_8-branch revision 202388]"
        .section    .note.GNU-stack,"",@progbits
    

    It's easy to tell that for foo it calls destructor, but for boo it directly calls delete [] function (_ZdaPv after name mangling). This also happens without optimizations. The code is longer, because methods are actually output, but it's still noticeable that delete [] is called directly for boo.

        .file   "deleter.cpp"
        .section    .text._ZN3fooD2Ev,"axG",@progbits,_ZN3fooD5Ev,comdat
        .align 2
        .weak   _ZN3fooD2Ev
        .type   _ZN3fooD2Ev, @function
    _ZN3fooD2Ev:
    .LFB4:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movq    %rdi, -8(%rbp)
        movq    -8(%rbp), %rax
        movl    $1, (%rax)
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    .LFE4:
        .size   _ZN3fooD2Ev, .-_ZN3fooD2Ev
        .weak   _ZN3fooD1Ev
        .set    _ZN3fooD1Ev,_ZN3fooD2Ev
        .text
        .globl  _Z10remove_fooP3foo
        .type   _Z10remove_fooP3foo, @function
    _Z10remove_fooP3foo:
    .LFB6:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        pushq   %rbx
        subq    $24, %rsp
        .cfi_offset 3, -24
        movq    %rdi, -24(%rbp)
        cmpq    $0, -24(%rbp)
        je  .L3
        movq    -24(%rbp), %rax
        subq    $8, %rax
        movq    (%rax), %rax
        leaq    0(,%rax,4), %rdx
        movq    -24(%rbp), %rax
        leaq    (%rdx,%rax), %rbx
    .L6:
        cmpq    -24(%rbp), %rbx
        je  .L5
        subq    $4, %rbx
        movq    %rbx, %rdi
        call    _ZN3fooD1Ev
        jmp .L6
    .L5:
        movq    -24(%rbp), %rax
        subq    $8, %rax
        movq    %rax, %rdi
        call    _ZdaPv
    .L3:
        addq    $24, %rsp
        popq    %rbx
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    .LFE6:
        .size   _Z10remove_fooP3foo, .-_Z10remove_fooP3foo
        .globl  _Z10remove_booP3boo
        .type   _Z10remove_booP3boo, @function
    _Z10remove_booP3boo:
    .LFB7:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movq    %rdi, -8(%rbp)
        cmpq    $0, -8(%rbp)
        je  .L7
        movq    -8(%rbp), %rax
        movq    %rax, %rdi
        call    _ZdaPv
    .L7:
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
    .LFE7:
        .size   _Z10remove_booP3boo, .-_Z10remove_booP3boo
        .ident  "GCC: (SUSE Linux) 4.8.1 20130909 [gcc-4_8-branch revision 202388]"
        .section    .note.GNU-stack,"",@progbits
    

    This also applies to new []. _Znam is called directly, without constructing objects, even without optimizations.

    Generally, that means custom constructors or destructors mean that new [] and delete [] won't be executed in constant time. But if there aren't, the compiler doesn't try to call constructors or destructors for these, and they will be POD data types, which means that constructing these objects is simple malloc-like call. There are some exceptions (involving various optimizations), but usually code with constructors/destructors will be O(N), and without will be O(1), assuming O(1) new []/delete [] implementation.

    0 讨论(0)
  • 2021-02-07 10:42

    But if I have a class, for which I have not defined any constructor/destructor, will the complexity still be O(n), or will it be just O(1)?

    The members themselves might still have destructors. In short, for PODs, delete[] will be O(1).

    0 讨论(0)
  • 2021-02-07 10:45
    MyClass *p = static_cast<MyClass*> (::operator new (sizeof(MyClass[N])));
    

    Allocates memory for N objects and does not construct them. In that way the complexity will be the same as malloc(). It will be obviously faster, then allocating and constructing objects of complex class.

    0 讨论(0)
  • 2021-02-07 10:52

    It depends on your exact syntax:

    auto x = new unsigned[2];
    auto y = new unsigned[2]();
    ::std::cout << x[0] << "\n" << x[1] << "\n" << y[0] << "\n" << y[1] << "\n";
    delete[] x;
    delete[] y;
    

    gives the output (on my machine):

    3452816845
    3452816845
    0
    0
    

    Because one will be default initialized and the other value initialized.

    delete[] on the other hand is even simpler to understand: If your data type has a destructor, it will be called. The built in (and thus POD) types generally do not.

    0 讨论(0)
提交回复
热议问题