Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Deleting an Object and setting the Pointer to NULL

6 views
Skip to first unread message

Robi-Wan-Kenobi

unread,
Mar 13, 2007, 7:06:44 PM3/13/07
to
I have a question i would like to discuss:

When we delete an object with the delete operator, the pointer to that
object remains keeping the address of that object which is no longer
valid. To me it would seem reasonable to put that pointer implicitly
to NULL . So it wouldn't harm if one used the delete for this pointer
for a second time. But neither C nor C++ is doing so. Is there a
special reason for this behaviour or is it just for historic reason?

I came to that question because we are using wrapper classes for
objects that belong to a third-party-library which we are using.

We are keep these wrapper object as Pointers. Now when I want to
delete one of these objects I call a function like:
AbcDeleteObject(ObjectWrapperClass* object);
object = NULL;
After doing so I might have to set that pointer to NULL .

So my idea was to pass the pointer in the Delete-function by reference
and set it to NULL implicitly like this:

AbcDeleteObject(ObjectWrapperClass*& object)

{

Api_delete_entity(object->theEntity());

delete object;

Object = NULL;

}

But to do so seems not to be very common in the C++ world. Is there a
special reason?

I am looking forward to your answer.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Niek Sanders

unread,
Mar 14, 2007, 5:25:33 AM3/14/07
to
On Mar 13, 4:06 pm, "Robi-Wan-Kenobi" <robert.ka...@gmx.de> wrote:
> I have a question i would like to discuss:
>
> When we delete an object with the delete operator, the pointer to that
> object remains keeping the address of that object which is no longer
> valid. To me it would seem reasonable to put that pointer implicitly
> to NULL . So it wouldn't harm if one used the delete for this pointer
> for a second time. But neither C nor C++ is doing so. Is there a
> special reason for this behaviour or is it just for historic reason?

Stroustrup explains this on his webpage:
http://www.research.att.com/~bs/bs_faq2.html#delete-zero


- Niek Sanders
http://www.cis.rit.edu/~njs8030/

Ivan Novick

unread,
Mar 14, 2007, 5:26:22 AM3/14/07
to
On Mar 13, 4:06 pm, "Robi-Wan-Kenobi" <robert.ka...@gmx.de> wrote:
> When we delete an object with the delete operator, the pointer to that
> object remains keeping the address of that object which is no longer
> valid. To me it would seem reasonable to put that pointer implicitly
> to NULL .
If you use a null pointer or a junk pointer both are bad. I can see
your point that using a null pointer is less bad.

> So it wouldn't harm if one used the delete for this pointer
> for a second time. But neither C nor C++ is doing so. Is there a
> special reason for this behaviour or is it just for historic reason?

IMHO I don't think there is a specific reason, but also I don't think
that it would be uniformly desirable by all parties in all cases.

> We are keep these wrapper object as Pointers. Now when I want to
> delete one of these objects I call a function like:
> AbcDeleteObject(ObjectWrapperClass* object);
> object = NULL;
> After doing so I might have to set that pointer to NULL .

Ok, thats fine, you can do that if you like in your app, but that
doesn't mean we have to change the language and all get the behavior
you want.

Regards,
Ivan Novick
http://www.0x4849.net

Jack Klein

unread,
Mar 14, 2007, 5:22:40 AM3/14/07
to
On Tue, 13 Mar 2007 17:06:44 CST, "Robi-Wan-Kenobi"
<robert...@gmx.de> wrote in comp.lang.c++.moderated:

> I have a question i would like to discuss:
>
> When we delete an object with the delete operator, the pointer to that
> object remains keeping the address of that object which is no longer
> valid. To me it would seem reasonable to put that pointer implicitly
> to NULL . So it wouldn't harm if one used the delete for this pointer
> for a second time. But neither C nor C++ is doing so. Is there a
> special reason for this behaviour or is it just for historic reason?

At least in the C case, free() takes a copy of the pointer object by
value. Any change it makes to its argument will have no effect on any
original in the calling function. In fact, the calling function might
not actually have an object with the original address, the expression
passed to free() might come from an operator that yields an rvalue.

> I came to that question because we are using wrapper classes for
> objects that belong to a third-party-library which we are using.
>
> We are keep these wrapper object as Pointers. Now when I want to
> delete one of these objects I call a function like:
> AbcDeleteObject(ObjectWrapperClass* object);
> object = NULL;
> After doing so I might have to set that pointer to NULL .
>
> So my idea was to pass the pointer in the Delete-function by reference
> and set it to NULL implicitly like this:
>
> AbcDeleteObject(ObjectWrapperClass*& object)
>
> {
>
> Api_delete_entity(object->theEntity());
>
> delete object;
>
> Object = NULL;
>
> }
>
> But to do so seems not to be very common in the C++ world. Is there a
> special reason?

I suppose the C++ operators delete and delete [] could be magically
redefined to take a reference to the pointer, instead of accepting it
by value as C's free does, and setting the pointer to NULL. But this
as the potential to break much code.

Consider:

my_type pointer = new my_type [3];
for (int i = 0; i < 3; ++i, ++pointer)
{
/* do something with *pointer */
}

delete [] pointer - 3;

This is valid, but would break if delete [] required a reference to a
modifiable object.

Setting the actual pointer free'd or deleted could actually cause
errors, because it could generate a false sense of security. There
might well be other pointers to the start of, or into, the allocated
memory and they would not be set to NULL.

Finally, C++ has adopted the C philosophy that you don't pay for what
you don't use. If you want to have your pointers automatically set to
NULL when deleted, write your own wrapper functions around the free or
delete. Then you pay the price of the extra operation(s) only when
you call those wrapper functions. Others who feel that they do not
need this, perhaps because of they way they handle their pointers, do
not have this extra run time overhead.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.contrib.andrew.cmu.edu/~ajo/docs/FAQ-acllc.html> I am


looking forward to your answer.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

James Kanze

unread,
Mar 16, 2007, 11:15:03 PM3/16/07
to
On Mar 14, 12:06 am, "Robi-Wan-Kenobi" <robert.ka...@gmx.de> wrote:
> I have a question i would like to discuss:

> When we delete an object with the delete operator, the pointer to that
> object remains keeping the address of that object which is no longer
> valid. To me it would seem reasonable to put that pointer implicitly
> to NULL . So it wouldn't harm if one used the delete for this pointer
> for a second time. But neither C nor C++ is doing so. Is there a
> special reason for this behaviour or is it just for historic reason?

There are several very important reasons:

-- The argument for delete is not required to be an lvalue---in
fact, in my code, it often isn't. And you can't set a
non-lvalue to NULL.

-- It doesn't buy you anything, anyway, since typically,
there's not just a single pointer to the object, but a
number of them.

-- Finally, most of the time when there is just a single
pointer, it is in an object, and the delete is in the
destructor, so there's no point---the pointer itself will
cease to exist.

> I came to that question because we are using wrapper classes for
> objects that belong to a third-party-library which we are using.

> We are keep these wrapper object as Pointers. Now when I want to
> delete one of these objects I call a function like:
> AbcDeleteObject(ObjectWrapperClass* object);
> object = NULL;
> After doing so I might have to set that pointer to NULL .

> So my idea was to pass the pointer in the Delete-function by reference
> and set it to NULL implicitly like this:

> AbcDeleteObject(ObjectWrapperClass*& object)
>
> {
> Api_delete_entity(object->theEntity());
> delete object;
> Object = NULL;
> }

> But to do so seems not to be very common in the C++ world. Is there a
> special reason?

I'd say that it is common in the few cases where it is relevant.
The reason why you don't see it much is that it isn't relevant
that often. The most frequent case which comes to my mind is
cached data, where:

if ( cachedDataPtr == NULL ) {
// cache invalid, create the data...
}

and

delete cachedDataPtr ;
cachedDataPtr = NULL ;

to invalidate the cache.

In practice, however, I find such cases to be fairly rare. In
my own code, delete this is probably the most frequent case (and
this isn't an lvalue, and can't be set to null), and a delete in
the destructor the second---between them, they cover well over
95% of my deletes, and nulling the pointer is either impossible
or irrelevant in either case.

Note too that in cases where nulling the pointer might be
relevant, smart pointers might be an even better solution.
Typically, for example, a boost::scoped_ptr might be used for
the cache, with "cachedDataPtr.reset()" to invalidate.

--
James Kanze (GABI Software) email:james...@gmail.com
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

Seungbeom Kim

unread,
Mar 17, 2007, 4:50:29 PM3/17/07
to
James Kanze wrote:
> There are several very important reasons:
>
> -- The argument for delete is not required to be an lvalue---in
> fact, in my code, it often isn't. And you can't set a
> non-lvalue to NULL.
>
> -- It doesn't buy you anything, anyway, since typically,
> there's not just a single pointer to the object, but a
> number of them.

Those are the essential reasons, I believe.

> -- Finally, most of the time when there is just a single
> pointer, it is in an object, and the delete is in the
> destructor, so there's no point---the pointer itself will
> cease to exist.

Except when the pointer is in a container, in which case you have to
explicitly set the pointer to null to avoid undefined behaviour, IIRC:

for (i = begin(); i != end(); ++i) delete *i, *i = 0;
or
for (i = begin(); i != end(); ++i) delete i->second, i->second = 0;

In the past I've written the following to avoid repeating the same code:

template <typename T>
inline void delete_ptr(const T*& p)
{ boost::checked_delete(p); p = 0; }

template <typename T>
struct deleter : public std::unary_function<const T*&, void>
{
void operator()(const T*& p) const { delete_ptr(p); }
};

// a function object that deletes the 'second' members of std::pair's
// using member template - no need to specify the template argument
// when using, but cannot derive from std::unary_function

struct mapped_ptr_deleter
{
template <typename T1, typename T2>
void operator()(std::pair<T1, T2>& p) const { delete_ptr(p.second);}
};

--
Seungbeom Kim

James Kanze

unread,
Mar 18, 2007, 4:34:30 PM3/18/07
to
On Mar 17, 9:50 pm, Seungbeom Kim <musip...@bawi.org> wrote:
> James Kanze wrote:
> > There are several very important reasons:

> > -- Finally, most of the time when there is just a single


> > pointer, it is in an object, and the delete is in the
> > destructor, so there's no point---the pointer itself will
> > cease to exist.

> Except when the pointer is in a container, in which case you have to
> explicitly set the pointer to null to avoid undefined behaviour,

In theory. (But of course, then it's not a case of "just a
single pointer", either.) In practice, it's not something I
worry about in production code.

And of course, the other solution would be to remove it first
from the container. Something like:

while ( ! myVector.empty() ) {
T* p = myVector.back() ;
myVector.pop_back() ;
delete p ;
}

More "in", of course, would be to use swap:

for ( std::vector< T >::iterator iter = myVector.begin() ;
iter != myVector.end() ;
++ iter ) {
T* p = NULL ;
std::swap( p, *iter ) ;
delete p ;
}

(I'm just joking, of course. See below for a more reasonable
solution.)

> IIRC:

> for (i = begin(); i != end(); ++i) delete *i, *i = 0;
> or
> for (i = begin(); i != end(); ++i) delete i->second, i->second = 0;

Formally, I think that even those have undefined behavior. You
have to remove the pointer from the container in some way
(removing the element or changing its value) *before* the
delete. The simple presence of the pointer in the container
after delete is undefined behavior, and presumable, the
operator*() called in the expression to null the pointer could
in fact trigger such undefined behavior.

> In the past I've written the following to avoid repeating the same code:

> template <typename T>
> inline void delete_ptr(const T*& p)
> { boost::checked_delete(p); p = 0; }

If you really care:

{
T* tmp = p ;
p = NULL ;
delete tmp ;
}

Otherwise, you've not eliminated the undefined behavior.

Note, however, that you're still not setting to null the operand
of the delete:-).

--
James Kanze (Gabi Software) email: james...@gmail.com


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

--

Lourens Veen

unread,
Mar 19, 2007, 3:16:52 PM3/19/07
to
Seungbeom Kim wrote:

> Except when the pointer is in a container, in which case you have to
> explicitly set the pointer to null to avoid undefined behaviour,
> IIRC:
>
> for (i = begin(); i != end(); ++i) delete *i, *i = 0;
> or
> for (i = begin(); i != end(); ++i) delete i->second, i->second
> = 0;

That's interesting. Why is it undefined behaviour to store an invalid
pointer in a container? The container doesn't need to dereference the
pointer does it? Or is the idea that there may be architectures (or
checking environmets?) where copying a pointer to a deleted object is
a problem?

Lourens


--

red floyd

unread,
Mar 19, 2007, 7:04:15 PM3/19/07
to
Lourens Veen wrote:
> Seungbeom Kim wrote:
>
>> Except when the pointer is in a container, in which case you have to
>> explicitly set the pointer to null to avoid undefined behaviour,
>> IIRC:
>>
>> for (i = begin(); i != end(); ++i) delete *i, *i = 0;
>> or
>> for (i = begin(); i != end(); ++i) delete i->second, i->second
>> = 0;
>
> That's interesting. Why is it undefined behaviour to store an invalid
> pointer in a container? The container doesn't need to dereference the
> pointer does it? Or is the idea that there may be architectures (or
> checking environmets?) where copying a pointer to a deleted object is
> a problem?
>

It's not UB to store an invalid pointer. The concept is that the
invalid pointer is hanging around, and can be accessed by some other
user of said container. By NULLLing out the pointer, you avoid that
possibility.

I think this is what Seungbeom was getting at, and he may have just
written it poorly.

James Kanze

unread,
Mar 20, 2007, 8:13:19 AM3/20/07
to
On Mar 20, 12:04 am, red floyd <no.s...@here.dude> wrote:
> Lourens Veen wrote:
> > Seungbeom Kim wrote:

> >> Except when the pointer is in a container, in which case you have to
> >> explicitly set the pointer to null to avoid undefined behaviour,
> >> IIRC:

> >> for (i = begin(); i != end(); ++i) delete *i, *i = 0;
> >> or
> >> for (i = begin(); i != end(); ++i) delete i->second, i->second
> >> = 0;

> > That's interesting. Why is it undefined behaviour to store an invalid
> > pointer in a container? The container doesn't need to dereference the
> > pointer does it? Or is the idea that there may be architectures (or
> > checking environmets?) where copying a pointer to a deleted object is
> > a problem?

> It's not UB to store an invalid pointer.

Oh yes it is. According to the standard, anyway; I would guess
that most people don't have to worry about it.

> The concept is that the
> invalid pointer is hanging around, and can be accessed by some other
> user of said container. By NULLLing out the pointer, you avoid that
> possibility.

> I think this is what Seungbeom was getting at, and he may have just
> written it poorly.

I don't think so. The issue has been raised here before.

--
James Kanze (GABI Software) email:james...@gmail.com
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

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

James Kanze

unread,
Mar 20, 2007, 8:12:47 AM3/20/07
to
On Mar 19, 8:16 pm, Lourens Veen <lour...@rainbowdesert.net> wrote:
> Seungbeom Kim wrote:
> > Except when the pointer is in a container, in which case you have to
> > explicitly set the pointer to null to avoid undefined behaviour,
> > IIRC:

> > for (i = begin(); i != end(); ++i) delete *i, *i = 0;
> > or
> > for (i = begin(); i != end(); ++i) delete i->second, i->second
> > = 0;

> That's interesting. Why is it undefined behaviour to store an invalid
> pointer in a container?

Because the standard says so. It says that all objects in a
container must be assignable and copiable; you can't copy (or
assign from) an invalid pointer.

> The container doesn't need to dereference the
> pointer does it? Or is the idea that there may be architectures (or
> checking environmets?) where copying a pointer to a deleted object is
> a problem?

Copying an invalid pointer is undefined behavior, because there
actually have been architectures where it generated a hardware
trap.

In practice, of course, if you don't do anything to increase the
size of the container, there will be no copy, so even in the
unlikely case where you are working on such an architecture,
you're probably safe if your looping to clean-up, and will
delete the container itself immediately afterwards. But the
standard doesn't make any restrictions as to when the objects
must be copiable and assignable; according to the standard, the
simple presence of such an object in the container (even if the
next operation removes it from the container) is undefined
behavior. This is also why Kim's code is formally incorrect;
between the time he does the delete, and the time he nulls the
pointer, there is an invalid pointer in the container.

--
James Kanze (GABI Software) email:james...@gmail.com
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

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

0 new messages