20

I don't get what following statement would do (specially second line)?

auto buff = std::make_unique<int[]>(128);
buff = std::make_unique<int[]>(512);

Will the second call to make_unique followed by assignment operator will de-allocate memory allocated by first call, or will there be memory leak? Must I have to use buff.reset(new int[512]); ?

I've debugged it, but didn't find any operator= being called, nor any destructor be invoked (by unique_ptr).

Ajay
  • 16,823
  • 9
  • 50
  • 94
  • I bet the compiler simply elided the first call. – kennytm Jun 14 '16 at 08:21
  • 1
    `operator=` should be called and the previously managed object will be deleted. No memory leak here. – songyuanyao Jun 14 '16 at 08:25
  • The declaration allocates an array of integer of size 512. See the destructor called here will use "delete buff" to deallocate memory but you have allocated memory for an array of integer so ideally "delete[] buff" should be called. For such requirement you can pass a custom deallocator with your smart pointer declaration. – sagar Jun 14 '16 at 08:26
  • 1
    An array smart pointer is rarely a good idea when you have `std::vector` at your disposal... – Christian Hackl Jun 14 '16 at 08:31
  • 2
    @sagar No, `unique_ptr` and `make_unique` are designed to work with array types so this this is fine (although `vector` would probably be better). – Chris Drew Jun 14 '16 at 08:32
  • @ChrisDrew Oops. Yes you are write. The custom deleter will be used with shared_ptr. I am sorry for the confusion. – sagar Jun 14 '16 at 08:34
  • Step back a minute and think. Would it make sense for a library type to produce a memory leak in such a straight-forward way? – Kerrek SB Jun 14 '16 at 09:27
  • @kennytm, I posted simplified code. – Ajay Jun 14 '16 at 09:33
  • @KerrekSB, I understand - but programmers may make mistakes. – Ajay Jun 14 '16 at 09:39
  • @ChristianHackl Then I wonder why `unique_ptr` would exist? – Ajay Jun 14 '16 at 09:40
  • 2
    @Ajay: I said "rarely", not "never"! :) See http://stackoverflow.com/questions/16711697/is-there-any-use-for-unique-ptr-with-array – Christian Hackl Jun 14 '16 at 09:43

3 Answers3

15

Move assignment operator is called, which does

if (this != &_Right)
{   // different, do the swap
    reset(_Right.release());
    this->get_deleter() = _STD move(_Right.get_deleter());
}

No leak here, as it does reset, which will deallocate.

V. Kravchenko
  • 1,753
  • 7
  • 11
8

There is no memory leak here, the assignment will deallocate the resources associated with the first allocation. Not seeing it in the debugger most likely means the relevant call was just optimised out. Try compiling with -O0 if you want to see it.

Smeeheey
  • 9,162
  • 16
  • 37
  • I am using VC++ 2015, I am debugging debug build. I saw `unique_ptr` class, and set breakpoints there - they get disabled (though other parts of `memory` header do hit the breakpoints. Hence, you must be right. – Ajay Jun 14 '16 at 09:41
3

gcc 5.3:

#include <memory>

extern void emit(int*);

int main()
{
    // declare and initialise buf
    auto buff = std::make_unique<int[]>(128);

    // make_unique on the RHS returns a temporary
    // - therefore an r-value reference

    // therefore this becomes and operator=(unique_ptr&&)
    // (move-assignment)
    buff = std::make_unique<int[]>(512);

    // something to get the compiler to emit code
    emit(buff.get());
}

yields assembly:

main:
        pushq   %r12
        movl    $512, %edi
        pushq   %rbp
        pushq   %rbx
        call    operator new[](unsigned long)  ; <-- new (1)
        movl    $64, %ecx
        movq    %rax, %rbp
        xorl    %eax, %eax
        movq    %rbp, %rdi
        rep stosq
        movl    $2048, %edi
        call    operator new[](unsigned long) ; <<-- new (2)
        movl    $2048, %edx
        xorl    %esi, %esi
        movq    %rax, %rdi
        movq    %rax, %rbx
        call    memset
        movq    %rbp, %rdi
        call    operator delete[](void*)       ;<-- delete (1)
        movq    %rbx, %rdi
        call    emit(int*)
        movq    %rbx, %rdi
        call    operator delete[](void*)       ;<-- delete (2)
        popq    %rbx
        xorl    %eax, %eax
        popq    %rbp
        popq    %r12
        ret
        movq    %rax, %r12
        movq    %rbp, %rbx
.L3:
        movq    %rbx, %rdi
        vzeroupper
        call    operator delete[](void*)       ;<-- handles a failed assignment
        movq    %r12, %rdi
        call    _Unwind_Resume
        movq    %rax, %r12
        jmp     .L3
Richard Hodges
  • 64,204
  • 6
  • 75
  • 124
  • 16
    An explaination of how smart pointers work and why there's no leak would have been more useful in my opinion than a bunch of compiler-specific assembly output. – gigabytes Jun 19 '16 at 08:56
  • 1
    @gigabytes forgive me. I learned to program in assembler in 1981. It was my first computer language so i tend to see every other language in terms of it. I assumed that annotated assembly would be the best explanation. What have I left unclear? – Richard Hodges Jun 19 '16 at 11:33
  • To conclude that these two lines indeed call `new` and `delete` as expected would have been sufficient to put `printf`statements in constructors and destructors, after ensuring the compiler was emitting code at all like you did, without resorting to asm. But to one asking this question, it is more useful to know *why* this is the case, that is, that the standard says that `unique_ptr` behaves that way, and the reasons why `unique_ptr` has been designed to behave that way. If you want some "under the hood" feeling, just post a possible implementation of `unique_ptr`. Just my 2 cents. – gigabytes Jun 19 '16 at 11:46
  • That is what V. Kravchenko's answer is doing, anyway. – gigabytes Jun 19 '16 at 11:49