-8

I have a class:

class A
{
public:
    void foo(void)
    {
        // equals to syntactically incorrect (&this == nullptr)
        if (*(void**)this == nullptr)
            return;
        ...
    }
};

I use it like this:

A * a = new A;
...

delete a;
...

a->foo();

Why the condition is true for release version only? If I switch to debug version, I usually get something like 0xFEEFEEFE.

EDIT

Ok guys. What you said is absolutely right, and I knew that. Undefined behavior is something that is not specified by standard. This is what every platform implements (but implements) in its own way.

The purpose of this question is to understand how is it implemented in Windows. This code always works properly ('always properly' means: on all versions of Windows (starting with xp) with any compiler the method is called and the statement is true)). In my case I want to know what usually happens if the object is deleted in Windows.

P.S. I should have asked this question in wasm.ru. Sorry.

John Smith
  • 109
  • 1
  • 7
  • 5
    Undefined behavior is undefined, don't do things that lead to it. – Some programmer dude Aug 29 '15 at 16:00
  • 3
    And no, `*(void**)this` does *not* equal `&this`. – Some programmer dude Aug 29 '15 at 16:01
  • The `a->foo()` invokes undefined behavior, so anything can happen. – Raymond Chen Aug 29 '15 at 16:02
  • 1
    Same theme is in play here as [in this other question](http://stackoverflow.com/q/6441218/335858). – Sergey Kalinichenko Aug 29 '15 at 16:03
  • The only half-way "correct" way would be testing `this` as-is, not dereferencing it after a typecase, but even so it's only mildly helpful, _if at all_ (I am involved in a project where we actually do that for a reason that nobody but the guy who put the checks in some years ago knows, but nobody is motivated to remove them either). The standard clearly states that you may not do anthing with `this` after deleting it (including looking at it, if you are pedantic!) so the member call alone is deep in UB land already, and any checks that happen thereafter are merely a shot in the dark... – Damon Aug 29 '15 at 16:11
  • "This code always works properly ('always properly' means: on all versions of Windows (starting with xp) with any compiler the method is called and the statement is true)). In my case I want to know what usually happens if the object is deleted in Windows." No, that's not true. It's possible that the value of `a` is predictable in various ways on windows with various compiler settings. But calling `a->foo()` is not legit in MSVC either and can cause "General Protection Fault". – Chris Beck Aug 29 '15 at 16:45
  • @ChrisBeck Undefined behavior **never** _works properly_! – πάντα ῥεῖ Aug 29 '15 at 18:12
  • (1) I don't know what statement of mine you are taking issue with. (2) If something is undefined by the C++ standard but supported by a nonstandard extension of MSVC, then it does indeed "work properly" under terms of the OP. The standard doesn't require that it "never works properly", it just doesn't guarantee that it will do anything in particular. – Chris Beck Aug 29 '15 at 18:17

2 Answers2

5

Why would this be true at all? It's unspecified what the value of a pointer is after deleting it. Implementations are not required to set pointers to null after deleting them. And really this should never be null unless you're already in crazy town.

Edit: After OP shows more code:

A * a = new A;
...

delete a;
...

a->foo();

Yeah, that line a->foo();? That's like the most surefire way to get undefined behavior.

Chris Beck
  • 15,614
  • 4
  • 51
  • 87
  • 1
    Why the downvote? This is the correct answer. It even warns against visits to crazy town. –  Aug 29 '15 at 16:01
  • To add to this correct answer, dereferencing `this` as validity check is also a sure way to invoke UB. Note that `*(void**)` does not just typecast `this` to a pointer-to-void-pointer but also dereferences the pointer. Which is crap if `this` is valid, and more crap as a "safety check" if it isn't. – Damon Aug 29 '15 at 16:06
  • Overusing safety checks often leads to slower and less safe code - true story. – Mateusz Grzejek Aug 29 '15 at 16:21
3

You assume that delete will put nullptr into variable that holds pointer being deleted. This is not true, delete is not doing things like that.

Why the condition is true for release version only? If I switch to debug version, I usually get something like 0xFEEFEEFE.

When you call delete a; under Windows, CRT library will mark memory under a pointer as freed. In debug mode it additionally puts into this memory region 0xFEEFEEFE values, which simply means freefree. This makes it easier to find during debugging cases when freed memory is being used. In release mode CRT is not puting this value after delete.

if (*(void**)this == nullptr)

In this case this pointer is declared as class-type * this, so casting it to (void**) is simply wrong.

a->foo();

this is wrong, after delete you should not use a - it causes UB

marcinj
  • 48,511
  • 9
  • 79
  • 100
  • Ok, and what about that zero-this. If CRT does not put the value `0xFEEFEFFF`, so maybe it puts zero? Why the call `a->foo()` does what it does? why the foo's code in memory is still referenced to deleted object? Or maybe it is not referenced. But why the call `a->foo()` turns me in the foo's body? – John Smith Aug 29 '15 at 17:06
  • It does not put 0xFEEEFEEE nor NULL into your `a` variable but, into memory pointed by `a`. You can read in this so: http://stackoverflow.com/questions/16585562/where-is-the-this-pointer-stored-in-computer-memory that `this` is mostly implemented as a hidden method parameter, so `this` inside your `foo` method call will be equal to address which is inside `a`, even if it is a dangling pointer. So you call will succeed, and you will see this to be the same pointer as before delete (even if this is invalid pointer), but your foo method will work on data that is kept in freed memory region.... – marcinj Aug 29 '15 at 17:26
  • 2
    ... this memory region might be the same as before calling delete for a long time. But some other allocation might make use of this region and put there some new data. This is UB and this is what should be avoided. – marcinj Aug 29 '15 at 17:28
  • In that thread you've suggested me said that `this` is not a variable. It is an expression. So, might there be some *table* of *this*'es? How long time does CRT 'tracks' for an object's `this`? I tried to delete my object, allocate a lot of stuff, write it, release it; but again, if I call that deleted `a->foo()`, I have a zero-this. I understand that the memory pointed by `a` is changed. But where that `this` is taken from to be passed into `foo`? – John Smith Aug 29 '15 at 18:22
  • @JohnSmith this is provided more like: `a->foo(a);`, so it is passed as a parameter, this also means that this is not stored with your class instance. There is no table of this'es. This pointers are not tracked explicitly by CRT, in your case `this` for your class instance is `a` variable, and its lifetime ends when main ends. `I have a zero-this` - I tried it and on Debug and Release this is never zero after delete - at least under VS2015. – marcinj Aug 30 '15 at 09:04
  • (1) thanks for `this` reference. (2) I tested it with MinGW 4.9.1, VS2008, VS2013. I had nulls. Well, I'm gonna try it with VS2015. Anyway, I will never use this check in my 'everyday coding' (as almost everyone realized first), but I believe it gives me a lot in understanding of Microsoft's implementation. – John Smith Aug 30 '15 at 17:58