0

Disclaimer: I know this is not how unique_ptr should be used, but for the sake of understanding, I would like to know what is going on here. Thank you!

Consider this function:

void foo(int* a) {
    unique_ptr<int> pointer_in_function(a);
}

And this main function:

int main(void) {
    int* myInt = new int(5);
    unique_ptr<int> pointer_in_main(myInt);

    foo(myInt);

    cout << *pointer_in_main << endl;
    cout << *pointer_in_main << endl;

    cin.get();
    return 0;
}

I consistently get the correct answer from the first cout. The second is undefined. The program sometimes crashes with a critical error on exit, but not always.

What I do not understand is why the first cout gives the correct answer consistently. Shouldn't the integer pointed to by myInt have been deleted when pointer_in_function went out of scope? Thank you for your help!

edit: As an aside, just to be sure, am I correct in my assumption that calling foo should delete my integer because pointer_in_function goes out of scope?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • 5
    There is no correct result. You have *undefined behaviour*. – juanchopanza Apr 30 '17 at 07:58
  • Sure, but shouldn't undefined behavior be a bit more... undefined? Is there any good reason the first cout works? –  Apr 30 '17 at 08:00
  • unique_ptr calls delete in destructor. So, second unique_ptr calls delete with UB because the object does not exists. – malchemist Apr 30 '17 at 08:01
  • 1
    @Thomas Marshall am I correct in my assumption that calling foo should delete my integer because pointer_in_function goes out of scope? yes – malchemist Apr 30 '17 at 08:03
  • 5
    @ThomasMarshall How can something be "more undefined"? It is undefined or it is not. – juanchopanza Apr 30 '17 at 08:03
  • @juanchopanza I guess what I mean is that if it's undefined, I wouldn't expect to get a consistent result the first time I dereference pointer_in_main. –  Apr 30 '17 at 08:06
  • 3
    @ThomasMarshall If it is undefined you cannot expect any particular outcome. All bets are off. I suggest reading up on undefined behaviour. – juanchopanza Apr 30 '17 at 08:07
  • Depends on the memory allocator your program uses. Most allocate more memory than you need, putting extra data in the allocated block. Like the size of the allocation and a pointer to the next allocation in the heap. When you release memory then it doesn't go through a lot of trouble, typically only updating the pointer to tie it into the free block list. It does not intentionally make your data invalid, that is extra work that should not be necessary if your program is not buggy. There are debug allocators around that do. – Hans Passant Apr 30 '17 at 08:09
  • 1
    consistent results is one particular way undefined behaviour can manifest – M.M Apr 30 '17 at 08:10
  • @HansPassant Thank you, that was exactly what I was looking for. –  Apr 30 '17 at 08:12
  • "_Is there any good reason the first cout works?_" Why wouldn't it? – curiousguy Apr 30 '17 at 08:53
  • Also see the answer to [Can a local variable's memory be accessed outside its scope?](http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope) – Bo Persson Apr 30 '17 at 09:16

2 Answers2

5

What I do not understand is why the first cout gives the correct answer consistently.

Most implementations do not clear memory after deleting or return it back to the OS, so if you inspect the memory quickly after deleting, the chances of it not having been overwritten yet are higher.

That's one of the valid consequences of undefined behaviour. It may be 100% predictable on your particular implementation, though it need not behave the same on others.

  • You should mention that it might work for quite a while - then a programmer comes along and does a little maintenance and things break – Ed Heal Apr 30 '17 at 08:13
1

It's not a good way to do (pure enough) experiments with constructors / destructors or deletion. If you have an object uncorrupted it DOESN'T imply that a delete operator hasn't been called (as its effect to the memory data is undefined and indeed many memory manager implementations don't change the released memory content immediately). Instead, create a class with explicit constructor(s) and destructor reporting their invocation and use it in place of int. As a destructor call prepends an object deletion (and as your code doesn't use stack or static allocation of an object under test, a destructor call is always implies a deletion), you may use destructor calls to trace deletions. Something like:

#include <iostream>
class MyObj {
    int value;
    MyObj(int v) : value(v) {std::cerr << "MyObj ctor called"<<std::endl;}
    ~MyObj() {std::cerr << "MyObj dtor called"<<std::endl;}
};

.....
int main (int argc, char **argv) {
    MyObj* myInt = new MyObj(5);
....
}
AndreyS Scherbakov
  • 2,674
  • 2
  • 20
  • 27