So now I'm wondering if there is anything in the C++ standard that
guarantees that a "delete this;" won't crash the program when the
function returns.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Yes. If that happens you aren't taking the proper precautions. You could
use a boost::shared_ptr "self" pointing to this to make the things
easier (when you call a function that could get to the delete this and
you need to access the object later you make a copy of the self shared_ptr
as a "deletion-preventing lock")
Saludos,
HoraPe
---
Horacio J. Peña
hor...@compendium.com.ar
hor...@uninet.edu
I don't know Standard specified or not. What I do know is that
such expression in destructor makes a recursive definition.
Beside, the use of "delete this" seems to require internal knowledge.
Neither of them is good (might work, though)
There's nothing in the Standard to guarantee that "delete this" won't
crash even as soon as it is called. After all, there is no guarantee
that the "this" inside a class method refers to a dynamically allocated
object in the first place.
The Standard does allow a program to return from a function call, so
returning from function will not - by itself - crash the program after
a "delete this" on a dynamically allocated object.
The risk, however, is that other code executed prior to, or at exit,
will reference the deleted object and then cause a crash. Not all code
that executes in C++ is apparent. Some code executes implicitly when
exiting a routine (the destructors for local objects are called). The
difficulty in ensuring that the deleted object will not again be
referenced - especially while still executing code in one of its member
functions - means that "delete this" should be used carefully, if it is
used at all.
Greg
Nobody can give you such guarantee, because it can crash. Even if your code
works otherwise, you may expect crash in VC7.1 if you compile with /GH and
_pexit function uses "this" pointer. Or code instrumented by your profiler
may crash, because it inserted code after your object committed suicide. You
may also have a crash when a local variable going out of scope used a copy
of this pointer. Or you overlooked that another member function calls your
suicide function and tries to do something with deceased object or there is
a recursion you didn't notice. Also the suicide function should never be in
the public interface where you can't have control over its use. In other
words you must be absolutely sure that the object is never accessed after
you killed it and that without any help from the compiler. That's too much a
burden with dubious benefits. You can always refactor your code so a suicide
won't be necessary. Return control to the outside and delete the object
safely.
- gene
B. Barnder schreef:
> I always understood using "delete this;" is a tricky but legal way for a
> object to commit suicide. All literature I have came accross suggest
> that with the proper precautions
> (http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.15) it
> should work. However recently I came across a C++ coding standard that
> recommended against using "delete this;" because "it is possible that
> the this pointer must be accessed when returning from the function."
>
> So now I'm wondering if there is anything in the C++ standard that
> guarantees that a "delete this;" won't crash the program when the
> function returns.
There's nothing in the standard that guarantees that a 'delete that'
won't crash the program either. It's considered common sense.
Technically, there is nothing which makes it Undefined Behavior. Now,
one can argue that this precise sequence isn't covered but you simply
can't cover all combinations of trivial statements. Since it's not
UB, it shall not crash.
Of course, one can still have tricky bugs. All members are gone
after a delete this, which can also invalidate pointers, references,
iterators etc. Pretty much all you have left is the state of the stack.
HTH,
Michiel Salters
class Base {
public:
virtual void DoSomething() { delete this; };
}
class Derived {
bool m_bCleanMe;
public:
void DoSomething() { /* do something*/
Base::DoSomething();
//now clean a member variable
m_bCleanMe = false;
}
}
The result of simply setting false value to your member variable will be
catastrophic!
This may seem a bit odd example, but this is exactly how one (very
popular though a bit outdated) UI framework works - the only difference
is that DoSomething method there is connected with UI widget destruction.
> So now I'm wondering if there is anything in the C++ standard that
> guarantees that a "delete this;" won't crash the program when the
> function returns.
No, I don't think that a standard can protect you from your own
mistakes. And using delete this; in a way that is not properly
documented and implemented is a mistake.
You can still try to assign 0 to this after it is deleted, but it won't
help at all if someone keep a *copy* of pointer to the object you deleted.
Hope this one helps,
Best regards,
Stan
> You can still try to assign 0 to this after it is deleted, but it
> won't
> help at all if someone keep a *copy* of pointer to the object you
> deleted.
'this' is a const pointer, so you cannot assign 0 to 'this'.
regards
--
jb
(reply address in rot13, unscramble first)
The biggest problem is that you do not know whether the object was
created on the heap or the stack (One of the things mentioned in the
FAQs). If you were to delete an object on stack, problems -
guaranteed. I can refer you to "More Effective C++" by Scott Meyers,
Item 27.
This boils down to the following (If I understand it correctly):
It is possible to determine whether an object was created by calling
operator new, and then react accordingly (i.e. delete this would also
be possible without undefined results - I believe Scott). On the other
hand, one would be able to beat the <delete this> problem by better
design... Best read it yourself as it is a long discussion.
Regards,
Werner
Stanimir Kabaivanov wrote:
> Hi B. Barnder,
> > I always understood using "delete this;" is a tricky but legal way for a
> > object to commit suicide. All literature I have came accross suggest
> > that with the proper precautions
> > (http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.15) it
> > should work. However recently I came across a C++ coding standard that
> > recommended against using "delete this;" because "it is possible that
> > the this pointer must be accessed when returning from the function."
> >
> IMHO this is a perfectly reasonable argument. When using delete this;
> you should at least put that in CAPITAL letters in your documentation.
> Otherwise someone that uses your class (method) might get an unpleasant
> surprise.
>
> > So now I'm wondering if there is anything in the C++ standard that
> > guarantees that a "delete this;" won't crash the program when the
> > function returns.
>
> No, I don't think that a standard can protect you from your own
> mistakes. And using delete this; in a way that is not properly
> documented and implemented is a mistake.
> You can still try to assign 0 to this after it is deleted, but it won't
> help at all if someone keep a *copy* of pointer to the object you deleted.
>
Hi.
I'd like to apologize in advance, but please stop teaching what is good
and what
is bad practice. BTW no one answered OP's question here - all the
opinions
are "DO NOT delete this. period." Let us review the question: is
'delete this'
guaranteered to work correctly IF all the requirements are met - i.e.
if
- object was allocated with 'new'
- this is the last member function called
- this function does not access object's members
- nothing stored reference to the deleted object
- and finally no obscure instrumentation occurred
Actually this question is a bit wider - can a member function safely
end it's object's lifetime. Consider following code:
class A
{
string name;
public:
A(const string& n);
void dispose();
}
map<string,A> gAmap;
A::A(const string& n): name(n) { gAmap[name]=*this; }
void A::dispose() { gAmap.erase(name); }
// or may be with extreme caution
// void A::dispose() { string n=name; gAmap.erase(n); }
int main(void)
{
A a("a");
...
gAmap["a"].dispose();
}
This approach is actually used, especially in multithreaded
environment, where dispose is responsible for terminating all
A's activities. After that we have to make a decision - use
approach, shown above - actually 'delete this' approach or
still keep an inconsistent, 'dead' object. Compared to arguments
from previous posts - IMO this is better to remove such objects
from the system and agree that storing a reference to the is illegal
than to keep such objects and build another level of confidence
that none of these object will be used ever.
After all does anyone know -
how "it is possible that
the this pointer must be accessed when returning from the function."
--
Peter A. Kerzum
Sure. The C++ standard defines what happens when you return from
a function - local objects are destructed, and the return value
is copied. As long as neither of these operations attempts to use
'this', by calling member function or accessing member data of the
deleted object, everything will be fine.
Laughing at myself, didn't I always use 'delete this' to implement
IUnknown::Release for COM development? I would say that's legitimate.
Regards,
Ben
You are absolutely right - I'm sorry for the mistake and that I did not
make it clear. What I had in mind, was that assigning 0 to this (even
with const_cast) can be later used to check whether the object is still
"alive". Like in the example below:
class T {
public:
T() {};
void doSomething() {
delete this;
const_cast<T*>(this) = 0;
if (this) {
m_bCleanMe = false;
};
};
bool m_bCleanMe;
};
But that's a very bad and weird code style of course :-).
Regards,
Stanimir Kabaivanov
Well, this is an interesting question.
Firstly, the standard doesn't require that something must be explicitly
designated as UB to be UB. It says (1.3.12): "Undefined behavior may also be
expected when this International Standard omits the description of any
explicit definition of behavior."
Secondly, reading 9.3.2/1 "In the body of a nonstatic (9.3) member function,
the keyword this is a non-lvalue expression whose value is the address of
the object for which the function is called." This may imply that "whose
value is" ALWAYS "the address ..." What was the intention of the original
authors is not clear from the passage. If "always" is implied there then
"this" pointer should be always valid, and since "delete this" invalidates
the pointer it's an UB.
Now, I understand we can parse the standard in different ways, and argue
until blue what was the original intent. From practical standpoint, even if
it's not UB, it should be avoided as risky behavior with no real benefits.
- gene
>>So now I'm wondering if there is anything in the C++ standard
>>that guarantees that a "delete this;" won't crash the program
>>when the function returns.
> I don't know Standard specified or not. What I do know is that
> such expression in destructor makes a recursive definition.
> Beside, the use of "delete this" seems to require internal
> knowledge.
Anything with this requires internal knowledge, by definition
(and in practice). 'delete this' probably requires less than
most things you do with this.
> Neither of them is good (might work, though)
I'm not sure what you are refering to by "neither". The only
thing I've seen mentionned is "delete this" (but I seem to be
missing parts of the thread). And there's nothing particularly
wrong with delete this -- it's perfectly logical for certain
types of objects (and not for others, obviously).
--
James Kanze mailto: james...@free.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
>>I always understood using "delete this;" is a tricky but legal
>>way for a object to commit suicide. All literature I have came
>>accross suggest that with the proper precautions
>>(http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.15)
>>it should work. However recently I came across a C++ coding
>>standard that recommended against using "delete this;" because
>>"it is possible that the this pointer must be accessed when
>>returning from the function."
> IMHO this is a perfectly reasonable argument. When using
> delete this; you should at least put that in CAPITAL letters
> in your documentation.
Only if it is your policy to put all post-conditions in capital
letters.
> Otherwise someone that uses your class (method) might get an
> unpleasant surprise.
Anyone who calls functions without bothering to find out what
the function is supposed to do is likely to get unpleasant
surprises. IMHO, there's not much you can do about it.
> Imagine:
> class Base {
> public:
> virtual void DoSomething() { delete this; };
> }
> class Derived {
> bool m_bCleanMe;
> public:
> void DoSomething() { /* do something*/
> Base::DoSomething();
> //now clean a member variable
> m_bCleanMe = false;
> }
> }
> The result of simply setting false value to your member
> variable will be catastrophic!
So can any number of other things you can do.
> This may seem a bit odd example, but this is exactly how one
> (very popular though a bit outdated) UI framework works - the
> only difference is that DoSomething method there is connected
> with UI widget destruction.
>>So now I'm wondering if there is anything in the C++ standard
>>that guarantees that a "delete this;" won't crash the program
>>when the function returns.
> No, I don't think that a standard can protect you from your
> own mistakes. And using delete this; in a way that is not
> properly documented and implemented is a mistake.
Well, we agree here. Using anything in a way that is not
properly documented and implemented is a mistake. If your point
is simply that calling a function without knowing why, or what
the function is supposed to do, is a bad idea, we agree
totally. But that's about it.
Whether we like it or not, whether we want to accept it or not,
the fact remains that for certain objects, certain functions
will render all further use of the object invalid. This has
nothing to do with delete, and is true in all programming
languages, even those without destructors or delete. Using the
object after that function has been called, be it from a
function in a derived class, or from elsewhere, won't work.
For a number of reasons, in C++, such functions use a particular
syntax, "delete this". But that is really just an
implementation detail. If the object is no longer valid, it is
no longer valid.
> You can still try to assign 0 to this after it is deleted,
> but it won't help at all if someone keep a *copy* of pointer
> to the object you deleted.
Well, it will ensure that the code will never core dump.
Because it will never compile:-).
--
James Kanze mailto: james...@free.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
>>I always understood using "delete this;" is a tricky but legal
>>way for a object to commit suicide. All literature I have came
>>accross suggest that with the proper precautions
>>(http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.15)
>>it should work. However recently I came across a C++ coding
>>standard that recommended against using "delete this;" because
>>"it is possible that the this pointer must be accessed when
>>returning from the function."
That, of course, is a recommendation against using delete,
period. Obviously, if you use the object after delete has been
called (whether via delete this, or by some other expression),
you're in trouble.
On the other hand, the final statement sounds a bit fishy. The
standard defines the semantics of return, and those semantics
don't include using the object any more than they include
setting all of its memory to 0.
>>So now I'm wondering if there is anything in the C++ standard
>>that guarantees that a "delete this;" won't crash the program
>>when the function returns.
> Nobody can give you such guarantee, because it can crash.
Not according to the standard. The standard is quite clear that
the only problem is if the object is used after delete. And the
standard also states pretty clearly (in §3.2/2) when an object
is "used", and when it is not.
> Even if your code works otherwise, you may expect crash in
> VC7.1 if you compile with /GH and _pexit function uses "this"
> pointer. Or code instrumented by your profiler may crash,
> because it inserted code after your object committed suicide.
If your compiler doesn't conform to the standard, you may,
obviously, expect crashes. How is this different than if delete
was called by an other function (which was called by a member
function).
> You may also have a crash when a local variable going out of
> scope used a copy of this pointer.
Or if anyone else uses a pointer to the object after the
delete. Regardless of delete this or not. That's C++.
> Or you overlooked that another member function calls your
> suicide function and tries to do something with deceased
> object or there is a recursion you didn't notice.
In general, functions have a contract -- preconditions and
postconditions. Anytime any function (member or not) calls your
function, and expects different postconditions that it
guarantees, you have a problem. I fail to see where delete this
changes anything in this. If you call functions without knowing
what they do, without having a real reason to do so, you've got
a problem.
> Also the suicide function should never be in the public
> interface where you can't have control over its use.
Actually, it most often is in the public interface, since it is
usually called as a result of some external stimulus.
If that external stimulus means that the object is no longer
valid, what are your alternatives?
> In other words you must be absolutely sure that the object is
> never accessed after you killed it and that without any help
> from the compiler. That's too much a burden with dubious
> benefits.
That's a burden that you have with every single delete that you
write. It's even a burden that won't go away (completely, at
least) with garbage collection -- if you attempt to use an
object after its logical lifetime has ended, your code won't
work.
> You can always refactor your code so a suicide won't
> be necessary. Return control to the outside and delete the
> object safely.
And what, exactly, does that change? Except maybe give you a
false sense of security.
--
James Kanze mailto: james...@free.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
I fail to see what is so risky about this. If the risk is that somebody will
access an object, either from outside or from inside, after "delete this"
has been called then it is no more dangerous than using std::vector;
std::vector<int> vec;
//oops
vec.front() = 3;
How does one know that front() cannot be called in such situation? By
reading the documentation of std::vector. How does std::vector implementor
know that he cannot call front() when the vector is empty? By being careful.
The same applies to "delete this". If my class has a member called Destroy()
which performs "delete this" I document that an object cannot be used after
this function is called and expect the users to follow the documentation.
Inside the class implementation I just have to be carefull.
As for "no real benefit" part, it is also not true. There is a very real
benefit in having an object to be responsible for its own lifetime.
Intrusive reference counting usually relies on this as well as remoting
solutions where an object may live in another process or machine and you
cannot destroy it in any way except by asking it to do so.
--
Eugene
http://www.gershnik.com
On the contrary, because it's a frequently used idiom for objects
which control their own lifetime, it's perfectly OK to use it
because implementors will make sure that it works.
No real benefits? It is a major benefit when your reference counted
object hits zero refs and you need to delete it.
Cheers,
Dave
I know that it's frequently used, for example, when deleting thread objects,
but it doesn't prove anything
(http://en.wikipedia.org/wiki/Appeal_to_tradition). You failed to prove that
it provides a) real benefits, b) it's reasonably safe.
I'm repeating myself. There are no real benefits, because it's not more
difficult to return control to another thread and delete the object directly
or use some way of garbage collecting.
> because implementors will make sure that it works.
If that were a valid argument then any, however dangerous, technique could
be justified by "implementors will make sure that it works." The technique
is dangerous, because a) there are many ways things may go wrong, b)
compiler will not alert you if things do go wrong. I already mentioned what
can go wrong, let me enumerate it here again:
a) local objects may contain copies of "this" and going out of scope crash.
b) instrumented code (by profiler, or _pexit in VC,...) can crash because it
may add a code at function exit.
c) it's easy to forget or overlook that a particular function committed
suicide, because one has to always remember the control paths that include
this function and keep in mind that this function should always be the last
that touches the object.
d) one must guarantee that this function is not invoked when exception is
thrown and the object is still must be deleted in another place.
e) if such function is in public interface, then unreasonable burden is
passed on the users of the class.
f) there is no way to guarantee that object was actually created with new.
There are possibly more, but I believe any of the above is sufficient to ban
this practice.
One may remember about all these precautions when he just wrote such a
function. But a month, a year later it's easy to forget and get a crash. I
know that happens from my personal experience. Don't plant mines in your
code that you or somebody else will likely step on in the future.
- gene
As I said, it's used in practice, and it works.
Implementors aren't going to be banning it, and
it's not forbidden by the Standard, and that's
really all that matters. If you personally don't
want to use it, don't.
-- Niklas Matthies
Yes, I think you are right here. <this> is a silent additional
argument and its signature is <most> probably something to the effect
of:
void T::nonStatic( T* const this )...
This would indeed mean that the value of the pointer will never be able
to change. Therefore : <delete this; this = 0;> would fail to compile.
> What was the intention of the original
> authors is not clear from the passage. If "always" is implied there then
> "this" pointer should be always valid, and since "delete this" invalidates
> the pointer it's an UB.
Note that "delete this" does not invalidate the pointer. It
invalidates the memory that the pointer points to. The pointer itself
is const, and cannot be invalidated, thats why <this = 0> won't
compile.
> Now, I understand we can parse the standard in different ways, and argue
> until blue what was the original intent. From practical standpoint, even if
> it's not UB, it should be avoided as risky behavior with no real benefits.
If the member function that performs the delete's intent is very clear,
it may not be as risky as you've mentioned. I must admit that
"deleting this" is one of the last options that I would use, though. A
good option, if one has to commit suicide, is to let the current stack
complete i.e. If you have some mechanism to post a msg (or even a
callback) over the context queue (like the win event loop), one gives
the current context a chance to complete. The event performing the
actual delete then performs the delete only, whereafter the next event
is obtained from the context queue(this may not make sense...). This
kind of falls in with the one responsibility idea. The next event in
the context queue's sole responsibility, it to delete the object
requesting deletion.
Werner
Gene Bushuyev wrote:
<snip>
> I know that happens from my personal experience. Don't plant mines in your
> code that you or somebody else will likely step on in the future.
>
> - gene
<snip>
Ok, but if personal experience is considered admissible in this debate
then my personal experience is that I've used libraries that use this
idiom and have *never* experienced any of the problems you describe.
Delegating the responsibility of deleting a ref counted object to
something other than the object itself sounds like a decidedly inferior
design to me and it doesn't solve any of the problems you enumerate.
Cheers,
Dave
Gene Bushuyev schreef:
> ... reading 9.3.2/1 "In the body of a nonstatic (9.3) member function,
> the keyword this is a non-lvalue expression whose value is the address of
> the object for which the function is called." This may imply that "whose
> value is" ALWAYS "the address ..." What was the intention of the original
> authors is not clear from the passage. If "always" is implied there then
> "this" pointer should be always valid, and since "delete this" invalidates
> the pointer it's an UB.
It's an expression. Expressions don't have values if you don't evaluate
them. This is critically important for volatiles (observable behavior),
but in this case it also applies. 9.3.2/1 refers to the value of (this)
when evaluated. Since (this) is not evaluated anymore after delete
this,
this requirement still holds.
BTW, even if (this) was a variable, it wouldn't mattera whole lot.
int* p = new int;
delete int;
int q = 0;
is legal too, even though the p pointer is no longer valid after
the delete. Use, and not existance of a deleted pointer is UB.
HTH,
Michiel Salters
> The biggest problem is that you do not know whether the object was
> created on the heap or the stack.
Wrong. About every delete ptr has this risk, but 'delete this' is a
positive exception. The reason is that a class employing 'delete this'
knows it shouldn't be allocated on the stack. In that case, it should
make its constructors private or protected, and provide a factory
interface.
HTH,
Michiel Salters
Yes, every delete ptr could have this risk, but usually pointers are
private variables of classes that create them (T* t = new T) and also,
knowing that they create them (and how they create them), take
responsibility for destroying them (in the appropriate way). Therefore
the risk is negated.
> The reason is that a class employing 'delete this'
> knows it shouldn't be allocated on the stack.
The class does'nt allocate itself, it is allocated from externally.
Therefore <knows it shouldn't be allocated> may be contented, as <it>
does not necessarily know who allocated it. Yes, we could make the
destructor protected to disallow allocation on stack. This does not
prevent someone from doing this:
class SuicidalBase //Some ref. counting mechanism causes self destruct.
{
public:
//...
protected:
~SuicidalBase()
{ //...
}
};
class BadDerived : public SuicidalBase
{
public:
~BadDerived()
{
//Oops, my base can be created on the stack now, sorry - he
// made it fairly easy for me to not pay attention.
}
};
> In that case, it should
> make its constructors private or protected,
This is actually fairly burdensome. Making the destructor protected is
less work than making all possible forms of construction
private/protected and has the same effect.
> and provide a factory interface.
I'm not sure what you mean by <providing a factory interface>. Don't
refer me to GOF' Design Patterns. My point remains that it is close to
impossible to prove (portable), from an internal perspective (from
within a member function) whether this class was actually created on
the stack or heap. This argument is not my argument, but was given by
S. Meyers in "More Effective C++", "Item 27" fairly elaborately.
Werner
> > Technically, there is nothing which makes it Undefined
> > Behavior. Now,
> Well, this is an interesting question.
> Firstly, the standard doesn't require that something must be
> explicitly designated as UB to be UB. It says (1.3.12):
> "Undefined behavior may also be expected when this
> International Standard omits the description of any explicit
> definition of behavior."
On the other hand, there seems to be a general agreement that if
the standard defines the semantics of something, and
implementation cannot do more. As a somewhat trivial example,
if I write ++ a, the implementation cannot modify any object
except that designated by a. (To tell the truth, I don't know
if, or where, in the standard, it actually says this. But
without it, you can't do anything.)
> Secondly, reading 9.3.2/1 "In the body of a nonstatic (9.3)
> member function, the keyword this is a non-lvalue expression
> whose value is the address of the object for which the
> function is called." This may imply that "whose value is"
> ALWAYS "the address ..." What was the intention of the
> original authors is not clear from the passage. If "always" is
> implied there then "this" pointer should be always valid, and
> since "delete this" invalidates the pointer it's an UB.
There's no doubt that "delete this" invalidates all future use
of the pointer (except in things like an expression to sizeof,
where the standard explicitly says that objects and values in
the expression will not be used). You don't even have to
dereference: something like:
delete this ;
std::cout << (void*)this << '\n' ;
is illegal.
> Now, I understand we can parse the standard in different ways,
> and argue until blue what was the original intent. From
> practical standpoint, even if it's not UB, it should be
> avoided as risky behavior with no real benefits.
Delete, in general, is risky behavior. The result is always a
dangling pointer. I don't see where the fact that the pointer
happened to be this changes anything, one way or the other.
And I'd call being able to make a class conform to the behavior
of the mode a real benefit. And I find it very hard to imagine
an OO design in which no class was ever responsible for its own
lifetime.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
In practice, it's trivially easy to always avoid "delete this"
by providing a template function:
template< typename T >
void deleteMe( T* p )
{
delete p ;
}
and having the function call this. Just as obviously, the
people opposing delete this would oppose this as well
(hopefully, anyway).
It does, however, serve to point out the falicy in trying to ban
delete this. Member functions call other functions, and IMHO,
the risk of those other functions deleting my object is often
higher than any risks involved with "delete this". In practice,
object lifetime is often part of the design, and when the object
lifetime is logically managed by the object, then any attempt to
avoid a delete this is simply obfuscation. The cure is worse
than the (purported) disease.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> > Secondly, reading 9.3.2/1 "In the body of a nonstatic (9.3)
> > member function, the keyword this is a non-lvalue expression
> > whose value is the address of the object for which the
> > function is called." This may imply that "whose value is"
> > ALWAYS "the address ..."
> Yes, I think you are right here. <this> is a silent
> additional argument and its signature is <most> probably
> something to the effect of:
> void T::nonStatic( T* const this )...
> This would indeed mean that the value of the pointer will
> never be able to change. Therefore : <delete this; this = 0;>
> would fail to compile.
So? Why would you want to null a pointer which is going to
disappear immediately?
> > What was the intention of the original authors is not clear
> > from the passage. If "always" is implied there then "this"
> > pointer should be always valid, and since "delete this"
> > invalidates the pointer it's an UB.
> Note that "delete this" does not invalidate the pointer.
Oh yes it does.
> It invalidates the memory that the pointer points to. The
> pointer itself is const, and cannot be invalidated, thats why
> <this = 0> won't compile.
You can't assign to this, but delete this very definitly does
invalidate the pointer. There are cases where this can be an
issue. Just like there are cases where the fact that delete p
invalidates p is an issue.
When you delete an object, via delete this or delete
somethingElse, you must ensure that no one ever uses the address
of that object. There's nothing special about delete this in
this regard, but it is something that must always be taken into
account. At every delete.
> > Now, I understand we can parse the standard in different
> > ways, and argue until blue what was the original
> > intent. From practical standpoint, even if it's not UB, it
> > should be avoided as risky behavior with no real benefits.
> If the member function that performs the delete's intent is
> very clear, it may not be as risky as you've mentioned.
In practice, it's no more risky than any of the alternatives.
> I must admit that "deleting this" is one of the last options
> that I would use, though. A good option, if one has to commit
> suicide, is to let the current stack complete i.e. If you have
> some mechanism to post a msg (or even a callback) over the
> context queue (like the win event loop), one gives the current
> context a chance to complete. The event performing the actual
> delete then performs the delete only, whereafter the next
> event is obtained from the context queue(this may not make
> sense...). This kind of falls in with the one responsibility
> idea. The next event in the context queue's sole
> responsibility, it to delete the object requesting deletion.
This is a good solution in the context of transactions. Delete
this shouldn't be used just anywhere. Anymore than delete p
should be. If you are using some sort of transaction, for
example, actual deletes cannot take place until commit, even
though you may have put the object into an unusable state
before.
Given this, I don't see any problem with a commit which more or
less does something like:
while ( ! objectList.empty() ) {
TransactionObject* p = objectList.back() ;
objectList.pop_back() ;
p->commit() ;
}
and Object::commit may terminate with delete this (depending on
what else was done in the transaction).
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> > The biggest problem is that you do not know whether the
> > object was created on the heap or the stack.
> Wrong. About every delete ptr has this risk, but 'delete this'
> is a positive exception. The reason is that a class employing
> 'delete this' knows it shouldn't be allocated on the stack. In
> that case, it should make its constructors private or
> protected, and provide a factory interface.
Or not. Typically, classes where delete this makes sense don't
make sense anywhere but on the heap. In other words, if someone
has even the slightest idea as to what the class' role is, it
simply wouldn't occur to them to create an instance on the
stack. So the issue is, in practice, a non-problem.
Of course, a factory interface is often useful for other things
as well.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
You're starting at the wrong end. Not all classes are "allocated
externally". In fact, some classes simply cannot be allocated
externally (private ctor). If you drop the incorrect assumption,
you see how some classes can use delete this safely.
> Yes, we could make the destructor protected to disallow allocation
> on stack. This does not prevent someone from doing this:
> class SuicidalBase //Some ref. counting mechanism causes self destruct.
> {
> public:
> //...
> protected:
> ~SuicidalBase()
> { //...
> }
> };
>
> class BadDerived : public SuicidalBase
> {
> public:
> ~BadDerived()
> {
> //Oops, my base can be created on the stack now, sorry - he
> // made it fairly easy for me to not pay attention.
> }
> };
>
> > In that case, it should
> > make its constructors private or protected,
>
> This is actually fairly burdensome. Making the destructor protected is
> less work than making all possible forms of construction
> private/protected and has the same effect.
Basically, my idea is simple: you make the dtor private. You're going
to call delete this; there's no need for other code to have access to
the dtor. If you don't trust the average programmer to read the docs,
make the ctor protected. If you don't even trust ppl writing a derived
clsas to read the docs, make the ctor private.
> > and provide a factory interface.
> I'm not sure what you mean by <providing a factory interface>. Don't
> refer me to GOF' Design Patterns. My point remains that it is close to
> impossible to prove (portable), from an internal perspective (from
> within a member function) whether this class was actually created on
> the stack or heap. This argument is not my argument, but was given by
> S. Meyers in "More Effective C++", "Item 27" fairly elaborately.
I WILL refer you to the GOF pattern, if only by copying it here. In the
Factory Pattern, all construction of a class is done by a factory. This
means all instances are created by the factory, and thus the factory
knows how each object is created. The simplest factory is a
"return new T".
Scott of course proves that IF a certain type can be put on the stack
and the heap, then it is impossible for a given object to determine
which of the two options was chosen. Note the distinction between
class and object. The essential precondition isn't met here, the
class I'm talking about doesn't allow heap creation. Therefore, for
each object I CAN say it wasn't created on the stack.
HTH,
Michiel Salters
> werasmus wrote:
> > Note that "delete this" does not invalidate the pointer.
k...@gabi-soft.fr wrote:
> Oh yes it does.
We are, I suspect misunderstanding one another. When I said "delete
this" does not invalidate the pointer, I actually meant the following
is possible and valid.
void T::fun(){ delete this; std::cout << this; }
Of course dereferencing <this> after deletion is fatal, but in the
context of what Gene said...
............"
Secondly, reading 9.3.2/1 "In the body of a nonstatic (9.3) member
function,
the keyword this is a non-lvalue expression whose value is the address
of
the object for which the function is called." This may imply that
"whose
value is" ALWAYS "the address ..." What was the intention of the
original
authors is not clear from the passage. If "always" is implied there
then
"this" pointer should be always valid, and since "delete this"
invalidates
the pointer it's an UB."...
... I was referring to the value of the actual pointer that is not
being invalidated. This corresponds with what the std refers to (whose
<value> is the address of ) and is misunderstood by him when he states
"This may imply...".
Werner
ka...@gabi-soft.fr schreef:
> Gene Bushuyev wrote:
> > "msalters" <Michiel...@logicacmg.com> wrote in message
> > news:1122024378....@o13g2000cwo.googlegroups.com...
> > ...
>
> > > Technically, there is nothing which makes it Undefined
> > > Behavior. Now,
>
> > Well, this is an interesting question.
>
> > Firstly, the standard doesn't require that something must be
> > explicitly designated as UB to be UB. It says (1.3.12):
> > "Undefined behavior may also be expected when this
> > International Standard omits the description of any explicit
> > definition of behavior."
>
> On the other hand, there seems to be a general agreement that if
> the standard defines the semantics of something, and
> implementation cannot do more. As a somewhat trivial example,
> if I write ++ a, the implementation cannot modify any object
> except that designated by a. (To tell the truth, I don't know
> if, or where, in the standard, it actually says this. But
> without it, you can't do anything.)
I guess it's the observable behavior part. E.g. if ++a overflows
into padding bits of b, b would be modified but the observable
value of b is still the same - and if a or b is volatile, even that
is not allowed.
HTH,
Michiel Salters
No, he understood you perfectly. Your code exhibits
undefined behavior. It is illegal to do anything with a
deleted pointer. Even reading its value is forbidden.
Hyman Rosen wrote:
> werasmus wrote:
> >>werasmus wrote:
> >>>Note that "delete this" does not invalidate the pointer.
> > k...@gabi-soft.fr wrote:
> >>Oh yes it does.
> >
> > We are, I suspect misunderstanding one another. When I said "delete
> > this" does not invalidate the pointer, I actually meant the following
> > is possible and valid.
> >
> > void T::fun(){ delete this; std::cout << this; }
>
> No, he understood you perfectly. Your code exhibits
> undefined behavior. It is illegal to do anything with a
> deleted pointer. Even reading its value is forbidden.
Based on what you are saying, this is then of course illegal...
MyClass* myInst( new MyClass );
delete myInst;
myInst = 0; //Illegal
//..or
std::cout << static_cast<void*>(myInst); //Illegal???
I think not. How does <this> then differ in that respect?
Werner
Nothing, as far as I know. If anyone knows about such thing, it would be
nice to post chapter and verse.
BTW delete this is not guaranteed to work, but by using common sense it can
be used. However, it will belong to the "implementation defined at best"
category. I did not read the whole standard now, but as far as I know it
does not specify when is an implementation allowed to access the memory
referenced by the this pointer. Therefore (if we behave as language
lawyers) we cannot assume its referred will not be accessed. However common
sense tells, that if a member function executes delete this; as its last
instruction, we are on the safe side. Compiler shops may be able to comment
on this one (for example if they do look for such statements), but IMHO it
is quite reasonable to assume that if there is no reason to access the
memory of the object, it won't be accessed.
--
WW aka Attila
:::
There is an old saying that if a million monkeys typed on a million
keyboards for a million years, eventually all the works of Shakespeare
would be produced. Now, thanks to Usenet, we know this is not true.
Is there actually *anything* in the standard which tells that the compiler
is not allowed to generate code, which accesses the memory area of the
object? I mean it is one thing that the standard defines what happens on
function return, but does it declare those as an exclusive list of what
*can* happen?
--
WW aka Attila
:::
Take as much as you want, put back more than you take.
Sure. 1.9/5.
1.9/5. "A conforming implementation executing a well-formed program shall
produce the same observable behavior as one of the possible execution
sequences of the corresponding instance of the abstract machine with the
same program and the same input."
The abstract machine doesn't go around touching arbitrary memory
willy-nilly, and therefore a real program can't do that either,
if it would affect the observable behavior.
No, that's fine. You can assign a new value to the deleted
pointer. It's touching the old value that's wrong.
> std::cout << static_cast<void*>(myInst); //Illegal???
Yes, that's illegal.
> I think not.
And yet, you are so very wrong. See 3.7.3.2/4.
Hyman Rosen wrote:
> werasmus wrote:
> > Based on what you are saying, this is then of course illegal...
> > MyClass* myInst( new MyClass );
> > delete myInst;
> > myInst = 0; //Illegal
>
> No, that's fine. You can assign a new value to the deleted
> pointer. It's touching the old value that's wrong.
>
> > std::cout << static_cast<void*>(myInst); //Illegal???
>
> Yes, that's illegal.
>
> > I think not.
>
> And yet, you are so very wrong. See 3.7.3.2/4.
I've looked and I am a little suprised. I can accept that passing the
value of the pointer to the deallocation function is undefined, because
it will attempt to de-allocate twice. Also, all other pointers
referring to the memory given back becomes invalid in the sense that
the memory they point to are invalid i.e. they cannot be dereferenced
for the purpose of calling member functions or accessing data. This
was how I understood it too.
What is hard to accept is that the actual value of the pointer cannot
be used as an integer, as the value of the pointer remains unchanged
after de-allocation (else the de-allocation function would take as
argument (void **p)... The value of the pointer is allocated on the
stack, right??? And therefore it remains a valid integer value until
the applicable stack unwinds. In my opinion, See 3.7.3.2/4. is not so
clear on this. They say "The effect of using an invalid pointer value
is undefined". This is certainly true if you use it for dereferencing
purposes. Certainly also if you pass it to the same deallocater twice.
But casting it to an integer (or a void*) and having a peep at the
value, it this also undefined? The pointer is invalidated in that it
can no longer be used as a pointer in the natural sense. As integer
alone (in other words - its value) it remains unchanged after
de-allocation. It is wrong to say this value cannot be used in an
integer sense? IOW the value is still valid. If this is the case, I
thank you for pointing this out to me.
Kind regards,
Werner
This is legal: you don't read the value of the deleted pointer,
you change it to become a new value.
> std::cout << static_cast<void*>(myInst); //Illegal???
This is illegal: reading the value of deleted pointer results
in undefined behavior.
> I think not.
The relevant quote is from basic.stc.dynamic.deallocation (3.7.3.2)
paragraph 4:
-4- If the argument given to a deallocation function in the standard
library is a pointer that is not the null pointer value (conv.ptr),
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.
There were discussions about this issue in the past and there is also an
open defect on this clause (it is unclear what the term "use" does mean
exactly). See e.g. the thread started by this article:
<http://groups-beta.google.com/group/comp.lang.c++.moderated/msg/a79fb51950cb61c3?hl=en&>.
> How does <this> then differ in that respect?
It isn't. You cannot access the pointer of any deleted object.
--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence
>White Wolf wrote:
>> Nothing, as far as I know. If anyone knows about such thing, it would be
>> nice to post chapter and verse.
>> it does not specify when is an implementation allowed to access the memory
>> referenced by the this pointer.
>
>1.9/5. "A conforming implementation executing a well-formed program shall
>produce the same observable behavior as one of the possible execution
>sequences of the corresponding instance of the abstract machine with the
>same program and the same input."
>
>The abstract machine doesn't go around touching arbitrary memory
>willy-nilly, and therefore a real program can't do that either,
>if it would affect the observable behavior.
I disagree. The implementation is allowed to use parts of an object for
its own purposes i.e. the arbitrary memory you refer to, but obviously
it's not allowed to access that memory after an object has been
destroyed unless it has "internal" knowledge that allows it to do it
safely without changing observable behaviour.
The question is : is the compiler allowed to assume that an object isn't
destroyed within a member function that's called on it? You could argue
that calling a member function on an object amounts to "using" the
object and destroying an object while it's still being used is UB so the
compiler is allowed to assume the object is still alive on all function
exits. Or you could argue that only a really weird compiler would
assume that an object won't be destroyed by a member function called on
it so it's not worth arguing about. IMO it's not clear cut when looked
at from a "legalese" point of view.
Graeme
> Hyman Rosen wrote:
> > werasmus wrote:
> > > Based on what you are saying, this is then of course illegal...
> > > MyClass* myInst( new MyClass );
> > > delete myInst;
> > > myInst = 0; //Illegal
> > No, that's fine. You can assign a new value to the deleted
> > pointer. It's touching the old value that's wrong.
Technically, I think it's the lvalue to rvalue conversion which
causes problems. Literally, the pointer is fine, but its value
isn't. As long as the expression doesn't use the value in
anyway, you're OK, so you can assign to the pointer, or take its
address, but you can't do anything which requires an lvalue to
rvalue conversion, nor can you increment or decrement it.
> > > std::cout << static_cast<void*>(myInst); //Illegal???
> > Yes, that's illegal.
> > > I think not.
> > And yet, you are so very wrong. See 3.7.3.2/4.
> I've looked and I am a little suprised. I can accept that
> passing the value of the pointer to the deallocation function
> is undefined, because it will attempt to de-allocate twice.
> Also, all other pointers referring to the memory given back
> becomes invalid in the sense that the memory they point to are
> invalid i.e. they cannot be dereferenced for the purpose of
> calling member functions or accessing data. This was how I
> understood it too.
> What is hard to accept is that the actual value of the pointer
> cannot be used as an integer, as the value of the pointer
> remains unchanged after de-allocation (else the de-allocation
> function would take as argument (void **p)...
> The value of the pointer is allocated on the stack, right???
The value of the pointer is a value, not an object. The pointer
is allocated on the stack (if the variable is an auto
variable). The pointer is still valid. The value that it
contains is not.
It's a subtle difference. Because the pointer (the object on
the stack) is still valid, I can assign to it, and take its
address. Because the value is no longer valid, however, I can
no longer use the value. Whether it comes from this pointer, or
some other pointer.
And while I don't think that the standard says so clearly, I'm
pretty sure that the intent is that no pointer into the deleted
object is valid. Thus, for example:
int * p = new int[ 10 ] ;
int * q = p + 5 ;
delete [] p ;
std::cout << p << '\n' ; // invalid
std::cout << q << '\n' ; // also invalid ?
> And therefore it remains a valid integer value until the
> applicable stack unwinds.
How can it be a valid integer value, when it is not an integer
value. And integer with the same bit pattern might be valid
(but that's not guaranteed, of course), but the pointer value is
invalid.
> In my opinion, See 3.7.3.2/4. is not so clear on this. They
> say "The effect of using an invalid pointer value is
> undefined". This is certainly true if you use it for
> dereferencing purposes.
The standard doesn't limit the invalidity in this way.
> Certainly also if you pass it to the same deallocater twice.
Which is the example given in the standard. An example,
however, not an exclusive list.
The issue was discussed at length in the C committee, prior to
the standardization of C. It's quite clear that the intent is
that an implementation may have different registers for
addresses and integers, that it can remap the memory so that
loading the address into an address register triggers a fatal
error, and that it can load the address into an address register
anytime it accesses the value. The current Intel 32 bit
hardware actually supports this, and there have been operating
systems for it which did as well.
> But casting it to an integer (or a void*) and having a peep
> at the value, it this also undefined?
Yep. To convert it, you've got to access the value.
Note that if you convert before the delete, the resulting
integral value doesn't become invalid. Because it's an integral
value, and not a pointer value. Note that memcpy'ing the value
is also legal, since it is always legal to access anything as an
array of bytes. Memcpy'ing it into a similar sized integer may
give an illegal integral value, however. (Of course, hardware
which supports illegal, trapping integral values isn't
particularly frequent. But it has existed.)
> The pointer is invalidated in that it can no longer be used as
> a pointer in the natural sense. As integer alone (in other
> words - its value) it remains unchanged after
> de-allocation.
Why do you keep talking about integers? A pointer is NOT an
integer. On some systems, it may sort of behave like one. On
others not.
> It is wrong to say this value cannot be used in an integer
> sense?
What does it mean to say that a pointer value can be used in an
integer sense? It doesn't even make sense before the delete
(and isn't allowed, either -- you can't multiply pointers, for
example).
C++ does have a possibility (reinterpret_cast) for the
implementation to define a conversion of a pointer value into an
integer value. But to convert the pointer value, you first have
to have a valid pointer value.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> And while I don't think that the standard says so clearly, I'm
> pretty sure that the intent is that no pointer into the deleted
> object is valid.
"[...] the deallocation function shall deallocate the storage referenced
by the pointer, rendering invalid all pointers referring to any part of
the deallocated storage."
"any part of the storage" - I think it is clear enough.
--
Maciej Sobczak : http://www.msobczak.com/
Programming : http://www.msobczak.com/prog/
> >White Wolf wrote:
> >> Nothing, as far as I know. If anyone knows about such
> >> thing, it would be nice to post chapter and verse. it does
> >> not specify when is an implementation allowed to access the
> >> memory referenced by the this pointer.
> >1.9/5. "A conforming implementation executing a well-formed
> >program shall produce the same observable behavior as one of
> >the possible execution sequences of the corresponding
> >instance of the abstract machine with the same program and
> >the same input."
> >The abstract machine doesn't go around touching arbitrary
> >memory willy-nilly, and therefore a real program can't do
> >that either, if it would affect the observable behavior.
> I disagree. The implementation is allowed to use parts of an
> object for its own purposes i.e. the arbitrary memory you
> refer to,
Is it? Other than by the "as if" rule.
> but obviously it's not allowed to access that memory after an
> object has been destroyed unless it has "internal" knowledge
> that allows it to do it safely without changing observable
> behaviour.
> The question is : is the compiler allowed to assume that an
> object isn't destroyed within a member function that's called
> on it? You could argue that calling a member function on an
> object amounts to "using" the object and destroying an object
> while it's still being used is UB so the compiler is allowed
> to assume the object is still alive on all function exits.
Calling a member function on an object is using the object.
(Actually, it is using . on the object, or -> on a pointer to
the object that is using the object.) Returning isn't.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
I mean none of them is good.
>The only thing I've seen mentionned is "delete this" (but I seem to be
>missing parts of the thread).
Same do I
>And there's nothing particularly
>wrong with delete this -- it's perfectly logical for certain
>types of objects (and not for others, obviously).
class T {
T() {..};
~T() { .... delete this; ....};
};
Don't you see weirdness? No need to resort to anything else to
conclude that it bad. If it happen to work, it is, IMO, by chance
of internal hacking know-how
I think that means that you agree, not disagree, with me,
since you are repeating what I said. The *abstract machine*
does not go around touching arbitrary bits of memory. The
implementation can do whatever it wants as long as it doesn't
change observable behavior.
> IMO it's not clear cut when looked at from a "legalese" point of view.
It's clear to me. The abstract machine description of C++ tells you
eaxctly what happens when language constructs are executed. After
executing 'delete this;', if the code doesn't execute anything which
uses the deallocated storage or pointers to it, then the code has not
engendered undefined behavior. The observable behavior of the actual
program must then mirror the execution of the abstract machine,
regardless of what goes on behind the scenes.
> > I disagree. The implementation is allowed to use parts of
> > an object for its own purposes i.e. the arbitrary memory you
> > refer to, but obviously it's not allowed to access that
> > memory after an object has been destroyed unless it has
> > "internal" knowledge that allows it to do it safely without
> > changing observable behaviour.
> I think that means that you agree, not disagree, with me,
> since you are repeating what I said. The *abstract machine*
> does not go around touching arbitrary bits of memory. The
> implementation can do whatever it wants as long as it doesn't
> change observable behavior.
I'm not so sure. I very much agreed with you to start with, but
on thinking it over...
I think it is fairly certain that the authors of the standard
intended to allow the classical vptr->vtbl implementation of
virtual functions. Which requires the implementation to go
around modifying parts of the class which I can't see, but which
must be there. Realistically, I don't expect to be able to call
delete this from the constructor of a base class, even if the
derived class constructor is a no-op. I don't expect it because
even if my derived class constructor is a no-op, the compiler
will generate code to set up the correct vptr after completing
the base class constructor. Even though the abstract machine
makes no mention of this.
Given the exact words in the standard, your interpretation seems
valid. It certainly seems the most reasonable interpretation
most of the time. But if it is the intended interpretation,
then the usual vptr->vtbl implementation is of doubtable
conformance.
Realistically, there's no real reason for a compiler to access
memory of the class in a return. Given the widespread use of
delete this (it is hard to imagine an OO program that doesn't
use it, either directly or indirectly), no compiler will dare
break code in order to do something it doesn't need to do. But
I'd feel much more happy about it if the standard were clearer.
As it is, although your interpretation is the obvious one, it
simply cannot be the intended one.
> > IMO it's not clear cut when looked at from a "legalese"
> > point of view.
> It's clear to me. The abstract machine description of C++
> tells you eaxctly what happens when language constructs are
> executed.
Sort of. But it doesn't say how. It doesn't say, for example,
that the compiler will modify the class object after the end of
a base class constructor, before entering into a derived class
constructor. All existing compilers do, however, because of the
way they implement the required semantics of virtual functions.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
As both the ctor and dtor are private I can see no weirdness and if the
ctor were public no experienced programmer would use delete this. The
use of delete this is normally restricted to classes whose instances
must go on the heap. There are well known idioms ensure that. Like many
aspects of safe programming, we cannot consider statements in isolation.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
>Graeme Prentice wrote:
>> On 26 Jul 2005 02:42:38 -0400, Hyman Rosen wrote:
>>>The abstract machine doesn't go around touching arbitrary memory
>>>willy-nilly, and therefore a real program can't do that either,
>>>if it would affect the observable behavior.
>>
>> I disagree. The implementation is allowed to use parts of an object for
>> its own purposes i.e. the arbitrary memory you refer to, but obviously
>> it's not allowed to access that memory after an object has been
>> destroyed unless it has "internal" knowledge that allows it to do it
>> safely without changing observable behaviour.
>
>I think that means that you agree, not disagree, with me,
>since you are repeating what I said. The *abstract machine*
>does not go around touching arbitrary bits of memory. The
>implementation can do whatever it wants as long as it doesn't
>change observable behavior.
ok, I partly agree with you in that any usage of memory "behind the
scenes" (or otherwise) by the implementation has to be with "valid"
memory, since it doesn't exist in the abstract machine, however if a
program has undefined behaviour (such as using an object after it's been
deleted) the implementation can access any memory it likes.
Your original comment suggested to me that an implementation can't touch
"hidden" parts of an object so I mistakenly responded accordingly.
>
>> IMO it's not clear cut when looked at from a "legalese" point of view.
>
>It's clear to me. The abstract machine description of C++ tells you
>eaxctly what happens when language constructs are executed. After
>executing 'delete this;', if the code doesn't execute anything which
>uses the deallocated storage or pointers to it, then the code has not
>engendered undefined behavior. The observable behavior of the actual
>program must then mirror the execution of the abstract machine,
>regardless of what goes on behind the scenes.
I consider doing "delete this" within a member function as slightly
weird and something that was probably never considered by the authors of
the standard. If they had (or did) consider(ed) it, would the standard
say anything different?
Graeme
I see why the standard dictates that this is undefined behavior. Does
this therefore imply that member variables that are pointers must always
be zeroed after deletion if that object is to be copied? For instance:
struct X {
int* p;
X() { p = new int(1); }
void foo() { delete p; }
};
X a,b;
b.foo();
a=b; // undefined behavior? I'm assuming yes.
What about unions?
struct X {
union {
int i;
int* p;
};
X() { p = new int(1); }
void foo() { delete p; }
};
X a,b;
b.foo();
a=b; // undefined behavior? or:
b.i = 2;
a=b; // undefined behavior?
Thanks,
Ethan Eade
Stanimir Kabaivanov wrote:
> What I had in mind, was that assigning 0 to this (even
> with const_cast) can be later used to check whether the object is still
> "alive". Like in the example below:
I am corinthing peanuts, alas...
> class T {
> public:
> T() {};
> void doSomething() {
> delete this;
> const_cast<T*>(this) = 0;
The cast produces a "copy" of this, that is an
r-value, not a l-value. You can't assign a new
value to the result of the cast.
What you probably meant, was:
*(const_cast<T**>(&this)) = 0;
And even this is undefined behaviour. this _is_ a
const "object", of type pointer to T. Modifying this
object, using the const_cast, is syntactically possible,
but leads to undefined behaviour, when the target of
the pointer (&this) actually is a const object.
Using the const_cast is valid (that is, defined behaviour),
when you have a pointer, that is declared as const pointer,
but in fact points to a non-const object. This happens,
when a previous cast has added that "const".
> if (this) {
> m_bCleanMe = false;
> };
Typical implementations of a compiler may argue:
this has been != 0 at the beginning of the function
call. The function even does not have to check that.
When this would be zero, the function must not be
called. All else is undefined behaviour, so the
compiler may assume that this is not zero.
this is const, so it may not be changed. It can
be changed, resulting to undefined behaviour, but
it may not. So at this point of execution, the
function may assume that still this != 0.
Thus, the compiler does not have to generate some
code to actually check the value of this. And I would
assume that nowadays compilers would omit this check,
at least when the simplest kind of optimization is
activated.
> };
> bool m_bCleanMe;
> };
>
> But that's a very bad and weird code style of course :-).
Indeed, and, very bad code, for it includes undefined behaviour.
Best regards,
Kurt.
This code has undefined behavior, as specified by 3.8/5.
You cannot do it because it's undefined behavior. 5.3.5/1 says
"The delete-expression operator destroys a most derived
object (1.8) or array created by a new-expression."
Therefore, you cannot call 'delete this;' in a constructor
because the object has not yet been created by the new-
expression. So the vtbl manipulation is fine; the object
may not be deallocated except by using the value returned
by the new-expression.
I disagree. For example, here's a link to a chapter from the book
"Essential COM", copyright 1997, which contains a 'delete this;',
<http://btobsearch.barnesandnoble.com/booksearch/isbninquiry.asp?btob=Y&ean=9780201634464&displayonly=CHP>
demonstrating that this technique was in unremarkable use before
the Standard was accepted.
It's an obvious technique for objects to manage their own lifetime
this way, and any discussion in committee would have resulted in
making sure that the standard allowed it.
It was certainly considered at the BSI (UK National Body level) because
I was a relatively inexperienced C++ programmer in the early 90s and I
raised making delete this ill-formed. At that point the UK experts took
the time not only to educate me but to seriously consider if there would
be any viable alternatives to its use if we were to make it ill-formed.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
And furthermore, it's explicitly declared to be undefined behavior
by 3.8/5, so there isn't even any thinking involved.
Well, what I know of is that after delete this, object exists no more.
It can't be in the member definition not causing semantically
contradictory, sufficient in isolation. If there is well known
idioms that 'delete this' is legal and safe, I'd like to hear.
I know little of such an idea and use.
Just do a Google search for '"delete this" reference count'
and you'll find tons of examples. Don't assume that just
because you haven't heard of something that it's wrong.
> > The issue was discussed at length in the C committee, prior
> > to the standardization of C. It's quite clear that the
> > intent is that an implementation may have different
> > registers for addresses and integers, that it can remap the
> > memory so that loading the address into an address register
> > triggers a fatal error, and that it can load the address
> > into an address register anytime it accesses the value. The
> > current Intel 32 bit hardware actually supports this, and
> > there have been operating systems for it which did as well.
> I see why the standard dictates that this is undefined
> behavior. Does this therefore imply that member variables
> that are pointers must always be zeroed after deletion if that
> object is to be copied?
More or less. You can, of course, implement an assignment
operator or a copy constructor which doesn't access the pointer.
This might even be a valid option if the pointer is part of a
complex structure caching some data, and you have a separate
attribute to indicate cache validity.
Most of the time, if the delete is not in the destructor, you'll
need some way of knowing whether the pointer is valid in later
functions. Setting it to null is usually the simplest and most
appropriate solution.
> For instance:
> struct X {
> int* p;
> X() { p = new int(1); }
> void foo() { delete p; }
> };
> X a,b;
> b.foo();
> a=b; // undefined behavior? I'm assuming yes.
Yes.
> What about unions?
A union contains the last element which was written. If this is
a pointer, and the pointer has since been deleted, the union is
(theoretically, at least) uncopiable.
> struct X {
> union {
> int i;
> int* p;
> };
> X() { p = new int(1); }
> void foo() { delete p; }
> };
> X a,b;
> b.foo();
> a=b; // undefined behavior? or:
Yes.
> b.i = 2;
> a=b; // undefined behavior?
No. The union contains a valid int.
Theoretically, what is copied in a union is only the last value
assigned to. In practice, of course, the compiler doesn't know
which one that is, and the restrictions on what can go into a
union have been carefully constructed to ensure that in fact,
bytewise copy (à la memcpy) works. And one of the rare things
that you can do with an invalid pointer value is memcpy it,
because you can memcpy any raw memory. So in practice, I can't
imagine any way copying a union with an invalid pointer could
really fail.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> >Don't you see weirdness? No need to resort to anything else
> >to conclude that it bad. If it happen to work, it is, IMO, by
> >chance of internal hacking know-how
> As both the ctor and dtor are private I can see no weirdness
> and if the ctor were public no experienced programmer would
> use delete this.
I think you missed his point. Private or public, no experienced
programmer would use delete this within a destructor.
> The use of delete this is normally restricted to classes whose
> instances must go on the heap. There are well known idioms
> ensure that.
My experience has been that it's generally not worth bothering
with such idioms. Any given class has a role in the
application. If you don't know the role, you can't use the
class. Period. The fact that certain classes only make sense
on the heap is generally so intimately associated with their
role that nobody who knew the role would ever thing of doing
otherwise.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
One extremely well-known idiom is "copy on write."
Here I demonstrate it with a simple string class.
class cowstring {
struct impl {
int refs;
char*data;
static char*kopy(const char*d) {
if (d) return strcpy(new char[1+strlen(d)], d);
else return strcpy(new char[1], "");
}
impl(const char*d) : refs(1),data(kopy(d)) {}
~impl() { delete data; }
impl*addref() { ++refs; return this;}
// *** Here's your "Delete this" statement!
void removeref() { if (!--refs) delete this; }
// *** That was your "Delete this" statement!
} *pimpl;
public:
cowstring(const char*x=0) : pimpl(new impl(x)) {}
cowstring(const cowstring&r) : pimpl(r.pimpl->addref()) {}
~cowstring() { pimpl->removeref(); }
cowstring&operator=(const cowstring&rhs) {
rhs.pimpl->addref();
pimpl->removeref();
pimpl=rhs.pimpl;
return *this;
}
cowstring&operator=(const char*rhs) {
pimpl->removeref();
pimpl = new impl(rhs);
return *this;
}
const char*tostring() { return pimpl->data; }
};
int main() {
{
cowstring c; // Default construct an empty string
cowstring d("Aardvark"); // Construct an aardvark
cowstring e(d); // Copy the aardvark
cowstring f("Moose"); // Construct a moose
c = f; // Assign the moose
f = d; // Assign the aardvark
std::cout << c.tostring() // Moose
<< '\n' << d.tostring() // Aardvark
<< '\n' << e.tostring() // Aardvark
<< '\n' << f.tostring() // Aardvark
<< std::endl;
// At this point, there are only two instances of
// cowstring::impl, one for Moose and one for Aardvark
c = e; // Assign the aardvark
// Now there is only one instance, for Aardvark
std::cout << c.tostring() // Aardvark
<< '\n' << d.tostring() // Aardvark
<< '\n' << e.tostring() // Aardvark
<< '\n' << f.tostring() // Aardvark
<< std::endl;
}
// Now that all the cowstrings are deleted,
// all the cowstring::impl's are deleted too.
}
Okay?
That's not what I was getting at, though, which is the following:
Suppose the standard would make it undefined behavior to execute a
non-static member function for an object that has been deleted.
(Where "execute" includes returning to or from such a function,
or throwing an exception "across" it.) My conjecture was that this
wouldn't constitute a fundamental impediment, at least not for the
typical uses of "delete this".
-- Niklas Matthies
Thanks for identifying extremely "well-known idiom" out of tons of
example for me.
With the instance, the point is how to interpret the definition
before execution leaving the entire definition scope, while the
object is no longer existant? This may be purely theoretical.
Similiar thing I know about occurs in constructor only, where special
rules are required. IMO, such code also requires special treatment.
It doesn't matter because the code of a class method is not tied
to any particular object. It's just code. If it doesn't touch any
member data or try to call any other non-static member methods then
it doesn't need its object to exist.
And the truth is that it doesn't have to call "delete this" in COW string
implementation. The pointer can be as easily destroyed from the outside.
Again, if you think it's so common and necessary, try searching the boost
library. You will find just two places: shared_count.hpp and
intrusive_ptr_test.cpp that have "delete this" in a similar shared pointer
scenario and similarly unnecessary.
The usual argument people have for "delete this" is a terminated thread
object destruction. All other cases are trivial. In case of a thread it's
still possible to pass control to another thread that can destroy the thread
object. So it's still unnecessary, but one can argue that it's convenient,
which is a pretty weak argument.
- gene
> > One extremely well-known idiom is "copy on write."
> > Here I demonstrate it with a simple string class.
> ...
> And the truth is that it doesn't have to call "delete this" in
> COW string implementation. The pointer can be as easily
> destroyed from the outside. Again, if you think it's so
> common and necessary, try searching the boost library. You
> will find just two places: shared_count.hpp and
> intrusive_ptr_test.cpp that have "delete this" in a similar
> shared pointer scenario and similarly unnecessary.
The Boost library is a collection of low level tools. The
essential need for delete this is at that application level,
where the classes model aspects of the (more or less) real
world. When a class itself manages its lifetime (in response to
external stimuli), delete this is the cleanest solution -- it
can always be avoided, and sometimes must be avoided, for
reasons of transactional security (it may be necessary to roll
back the delete), but if this is not the case, delete this
remains the cleanest, safest and most easily understood idiom.
> The usual argument people have for "delete this" is a
> terminated thread object destruction.
I've never heard that one.
> All other cases are trivial. In case of a thread it's still
> possible to pass control to another thread that can destroy
> the thread object. So it's still unnecessary, but one can
> argue that it's convenient, which is a pretty weak argument.
One can also argue that it is clearer and easier to understand,
because it corresponds closest to what one really wants to do.
Which is a pretty strong argument. (One could even argue that
deletion is a mutating operation, involves changing the value of
private attributes, and as such, should only be invoked from
within the object. I wouldn't go that far, however.)
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
>>Graeme Prentice wrote:
It's reassuring to hear that the committee (or at least part of
it) did consider the issue, and that there was explicit intent
to support it. IMHO, it wouldn't hurt if the standard made it
clearer that delete this is legal.
For the rest, I'll admit that the arguments concerning COW and
such don't convince me -- delete this in this context seems
nothing more than being overly clever. What does convince me is
that at the application level, good OO design frequently calls
for an object to manage its own lifetime. In such cases, delete
this is the natural expression of this fact. And none of the
alternatives really change the issue: if the post-condition of
the function is that the object it is called on doesn't exist
any more, then it mustn't exist -- whether delete this was used,
or some other function was called to do the delete. In many
ways, avoiding delete this is very much like assigning NULL to a
pointer after a delete: it gives a false sense of security, and
nothing else.
--
James Kanze mailto: james...@free.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
>It doesn't matter because the code of a class method is not tied
>to any particular object. It's just code.
I am regarding it definition code because this is in OO realm.
It it hard for me to know and predict what will be performed
while execution leaves the definition block.
>If it doesn't touch any
>member data or try to call any other non-static member methods
>then it doesn't need its object to exist.
You probably need Standard backup, which I dont't know.
> Stanimir Kabaivanov wrote:
>>What I had in mind, was that assigning 0 to this (even with
>>const_cast) can be later used to check whether the object is
>>still "alive". Like in the example below:
> I am corinthing peanuts, alas...
>>class T {
>>public:
>> T() {};
>> void doSomething() {
>> delete this;
>> const_cast<T*>(this) = 0;
> The cast produces a "copy" of this, that is an r-value, not a
> l-value. You can't assign a new value to the result of the
> cast.
> What you probably meant, was:
> *(const_cast<T**>(&this)) = 0;
You can't do this either. "this" is not an lvalue, so you can't
take its address.
--
James Kanze mailto: james...@free.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
Btw, this is the only place in the Standard where it uses a term
"non-lvalue." Is it different from rvalue? If it's the same, why the new
term? If it's different, then the Standard should have defined what
non-lvalue means.
Just curious.
- gene
And I have it, as I've posted in several messages already.
> Btw, this is the only place in the Standard where it uses a
> term "non-lvalue." Is it different from rvalue? If it's the
> same, why the new term? If it's different, then the Standard
> should have defined what non-lvalue means.
I don't think that there's any difference. I don't think that
the C standard uses the term rvalue -- things are either an
lvalue, or they're not. At some point in the drafting of the
C++, it was decided that having a name for things that are not
lvalues would make things clearer; I suspect that this is just a
case where the correction in terminology got overlooked.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
The object that contains `delete this' is simply not allowed to be put
on stack. You can declare its destructor private to disallow using it
as an auto variable.
Best regards,
Yongwei