views:

697

answers:

7

In this paragraph of C++ FAQ usage of delete this construct is discussed. 4 restrictions are listed.

Restrictions 1 to 3 look quite reasonable. But why is restriction 4 there that I "must not examine it, compare it with another pointer, compare it with NULL, print it, cast it, do anything with it"?

I mean this is yet another pointer. Why can't I reinterpret_cast it to an int or call printf() to output its value?

+3  A: 

because any action you can take with that pointer could trigger logic which is interpreted on the class methods of that object, which could lead to a crash.

Now, some of the actions you point at could be apparently "safe", but it's difficult to say what happens within any method you can call.

From the post: "must not examine it, compare it with another pointer, compare it with NULL, print it, cast it, do anything with it"?

All these actions can trigger operator related functions, which are evaluated with an undefined pointer. Idem for casting.

Now if you perform a reintepret_cast, that's probably a different story, and you could probably get along with it, as reinterpret is just a bit by bit reinterpretation, without involving (as far as I know) any method call.

Stefano Borini
Why could this prevent me from printing "this" value out?
sharptooth
nothing, you can print the crude value if you want I guess, but you have to be very, very careful.
Stefano Borini
What is an "operator related function"?
Roger Pate
I mean any function calling TheClass::operatorsomething()
Stefano Borini
+1  A: 

b/c the address that this refers to now, it undefined, and you don't know what might be there...

Dani
Why could this prevent me from printing "this" value out?
sharptooth
You can print the value of this, but it means nothing.
Dani
+2  A: 

For the same reason you would not delete any other pointer and then try and perform any operations on it.

DanDan
I would definitely be able to printf() a pointer value after that. But the FAQ says it shouldn'be done on "this".
sharptooth
Fair enough, if you really want to :) I suppose the FAQ is just being over-cautious.
DanDan
sharptooth, printing the value of a freed pointer triggers undefined behavior, just like printing freed `this`.
avakar
+10  A: 

The reason that you cannot do anything with a pointer after you delete it (this, or any other pointer), is that the hardware could (and some older machines did) trap trying to load an invalid memory address into a register. Even though it may be fine on all modern hardware, the standard says that the only thing that you can do to a invalid pointer (uninitialized or deleted), is to assign to it (either NULL, or from another valid pointer).

KeithB
but wouldn't that be applicable for all pointers? why only for `this`?
Naveen
yes, it is applicable to all pointers
jk
I'll accept this answer because it explains why exactly hell can break loose, not just cites The Standard.
sharptooth
+32  A: 

The value of 'this' after calling delete is undefined, and the behaviour of anything you do with it is also undefined. While I would expect most compilers to do something sensible, there's nothing (in the spec) stopping the compiler from deciding that its behaviour in this particular case will be emit code to format your hard-disk. Invoking undefined behaviour is (almost) always a mistake, even when your particular compiler behaves in the way you'd like it to.

You could work around this by taking a copy of the pointer (as an integer) before calling delete.

Andrew Aylett
+1, the only correct answer here.
avakar
Additionally, the way the compiler behaves today might not be the same as the next version does. Undefined really means "not guaranteed, ever".
Lasse V. Karlsen
+1, definitely the *correct* answer. To extend Lasse's response... _"undefined"_ also implies _likely to be inconsistent_.
D.Shawley
Although you could take the integer value of the pointer before deleting it - its a good rule of thumb that if you're circumventing the language's mechanics to tread into "undefined behavior" territory - that you're just asking for trouble, and a better fundamental design to your code should be considered.
Mordachai
Emitting opcodes for formatting the hard drive? And don't type Google into Google, not even for fun, it could *break* the internet.
Cecil Has a Name
It'd be standards compliant, that's all I said :). See also http://www.catb.org/jargon/html/N/nasal-demons.html
Andrew Aylett
I'm confused. How is the value of `this` undefined? It's the same value it was before, just like any other pointer.
GMan
@GMan: If you look at Steve Jessop's answer, you'll see the part of the spec that applies. It's not that the values *will* magically change, but that the spec doesn't define what will happen when the value is used, so the compiler doesn't have to worry about doing something sensible or consistent. Most of the compilers I've seen would maintain the value, as breaking horribly would be more effort, but you shouldn't rely on it. Some will raise a warning (or error) as part of, say, MISRA C checking.
Andrew Aylett
+20  A: 

Aha!

3.7.3.2/4: "... the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined".

Note that this says "using the value", not "dereferencing the pointer".

That paragraph is not specific to this, it applies to anything that has been deleted.

Steve Jessop
+1: even more direct than axa's answer. Very nice find Steve.
D.Shawley
so that means the normal practice of `delete p; p = NULL;` invokes undefined behavior?
Naveen
@naveen, no. p = NULL is still valid. We are changing the pointer to a new location, not using the value of the delete pointer. If it were otherwise, all pointer variables would only be able to be used once.
caspin
Agree. `p = NULL` is fine, but `p = p - p;` is not
Steve Jessop
`p = p - p;` is never valid :)
avakar
Why not? `p - p` is 0 in some kind of integer type, and a 0 integer is guranteed to convert to a NULL pointer. Or maybe I'm wrong, but hopefully you get the general idea. `p = p;`, if you like, or `p = p ? p : NULL;`
Steve Jessop
Are comparisons not included in the word "using"? If they are included, then `if (p == NULL)` would only be valid if p is, in fact, `NULL` - a rather strange situation.
Michael Myers
`p-p` is "zero the integer" not "zero the null pointer constant". The former won't convert to a null pointer whereas the latter will.
avakar
@mmyers: the expression is valid if p is valid. Not that strange...
jon hanson
@avaker. Oh yes, I see. `p-p` is an integer, but it's not a constant. What I missed is: 4.10/1 only requires that integral constant expressions which are 0 can be converted to pointers, not all integers that happens to be 0. g++ -pedantic doesn't catch that one. So `const int i = 0; char *p = i;` is valid, and yields a null pointer value in p. But `int i = 0; char *p = 0;` is invalid (silently so, in gcc). What fun.
Steve Jessop
That's because after `const int i = 0`, `i` is a constant expression that evaluates to zero and, therefore, converts to a null pointer value. In the latter example, `i` is not a constant expression and an assignment to a pointer is ill-formed (and thus shouldn't compile). You say that gcc didn't diagnose the error -- are you sure you had `p=i` in your source text? You wrote `p=0` in the comment above.
avakar
In fact I do get a warning (not an error) from gcc for `p = i;`, whether i is const or not, and whether for initialization or assignment. I didn't test that before, I just assumed that since I didn't get a warning for `p - p`, I wouldn't for other assignments from integers. I'm confused now how gcc is doing its diagnosis, but it's not rejecting any of it.
Steve Jessop
Strange, I get no warning for the former case (save for "unused variable ‘p’") and an error for the latter. I have g++-4.3.2, building with "g++ -ansi -pedantic -Wall".
avakar
A: 

In a multi-threaded program, the moment you delete a pointer, the free space can be allocated by another thread, overwriting the space used by this. Even in a single-thread program, unless you're very careful about what you call before returning, anything you do after delete this could allocate memory and overwrite what used to be pointed to by this.

In a Microsoft Visual C++ executable compiled in Debug mode, deleteing a pointer causes its memory to be immediately overwritten with a 0xCC test pattern (uninitialized variables are also initialized with this pattern), to help in identifying dangling pointer bugs such as this one.

This reminds me of when I fixed a bug in a online-playable game in which a Fire object's constructor deleted the oldest Fire if the total number of Fires had reached a certain number. The deleted Fire was sometimes the parent Fire creating a new Fire — bam, dangling pointer bug! It was only due to luck that this bug interacted with the memory allocation algorithm in a completely predictable way (the deleted Fire was always overwritten with a new Fire in the same way) — otherwise it would have caused a desynchronization between online players. I found this bug when rewriting the way the game did memory allocation. Due to its predictability, when I fixed it, I was also able to implement emulation of its behavior for compatibility with older game clients.

Deadcode