Detecting object deletion while in a loop

  • Thread starter Morten Aune Lyrstad
  • Start date
M

Morten Aune Lyrstad

Here's the deal: I'm looping through a collection of listener class
objects like this: (illustration code only)

AClass::ThrowEvent()
{
for (int i = 0; i < numListeners; i++)
{
listener.OnEvent();
}
}

AClass::Destroy()
{
if (listener) {
numListeners = 0;
delete listener;
listener = null;
}
}

The listener array consists of a derivation of a listener class.

The OnEvent call may actually end up destroying the AClass object, by a
call to AClass::Destroy().

What I need is a way to end the loop if this function is called, because
the Destroy will invalidate the listener array. In worst case scenarios,
even the AClass object itself might be invalidated. Does anyone have an
idea on how I can accomplish this? I would prefer that the creator of
the listener classes did not have to consider this.

Yours,
Morten Aune Lyrstad
 
V

Victor Bazarov

Morten said:
Here's the deal: I'm looping through a collection of listener class
objects like this: (illustration code only)

AClass::ThrowEvent()
{
for (int i = 0; i < numListeners; i++)
{
listener.OnEvent();
}
}

AClass::Destroy()
{
if (listener) {
numListeners = 0;
delete listener;
listener = null;
}
}

The listener array consists of a derivation of a listener class.

The OnEvent call may actually end up destroying the AClass object, by a
call to AClass::Destroy().

What I need is a way to end the loop if this function is called, because
the Destroy will invalidate the listener array. In worst case scenarios,
even the AClass object itself might be invalidated. Does anyone have an
idea on how I can accomplish this? I would prefer that the creator of
the listener classes did not have to consider this.


Exceptions are known to be used for that:

AClass::ThrowEvent()
{
for (int i = 0; i < numListeners; i++)
{
try {
listener.OnEvent();
}
catch (const deletion_indicator&) {
break;
}
}
}

AClass::Destroy()
{
if (listener) {
numListeners = 0;
delete listener;
listener = null;

throw deletion_indicator;
}
}

Of course, you need to define the type 'deletion_indicator' inside the
AClass. Customize its behaviour if you need to know more how the deletion
was accomplished or what happened along with it...

HTH

V
 
V

Victor Bazarov

Victor said:
Morten said:
Here's the deal: I'm looping through a collection of listener class
objects like this: (illustration code only)

AClass::ThrowEvent()
{
for (int i = 0; i < numListeners; i++)
{
listener.OnEvent();
}
}

AClass::Destroy()
{
if (listener) {
numListeners = 0;
delete listener;
listener = null;
}
}

The listener array consists of a derivation of a listener class.

The OnEvent call may actually end up destroying the AClass object, by
a call to AClass::Destroy().

What I need is a way to end the loop if this function is called,
because the Destroy will invalidate the listener array. In worst case
scenarios, even the AClass object itself might be invalidated. Does
anyone have an idea on how I can accomplish this? I would prefer that
the creator of the listener classes did not have to consider this.



Exceptions are known to be used for that:
[...]


Replying to myself: this is probably nonsense. Since you need the code
to finish up the processing normally (instead of jumping out of the
'OnEvent' function, you should actually just use a flag.

Sorry to confuse the issue.

Victor
 
M

Morten Aune Lyrstad

Thank you. I /really/ should have thought about that... ;-)
That got me alot further. However, as I said, the object itself may also
be deleted, with the operator delete. Then the Destroy function is
called in the destructor. And if I throw an exception then; well... that
just won't work. Any solution to that?
 
V

Victor Bazarov

Morten said:
Thank you. I /really/ should have thought about that... ;-)
That got me alot further. However, as I said, the object itself may also
be deleted, with the operator delete. Then the Destroy function is
called in the destructor. And if I throw an exception then; well... that
just won't work. Any solution to that?

(a) First, I don't think now that the exception approach is a good one.

(b) If you do want to use exceptions, throw it _outside_ the d-tor, in the
function that destroys the object and catch it _outside_ the loop, in
the caller to your 'ThrowEvent' member (or outside of it, etc.)

Victor
 
M

Morten Aune Lyrstad

Thank you so much for trying. I see no other option but to redesign my
library.
 
I

Ivan Vecerina

Morten Aune Lyrstad said:
Here's the deal: I'm looping through a collection of listener class
objects like this: (illustration code only)

AClass::ThrowEvent()
{
for (int i = 0; i < numListeners; i++)
{
listener.OnEvent();
}
}

AClass::Destroy()
{
if (listener) {
numListeners = 0;
delete listener;

I think you should be using some collection of the standard
C++ library -- maybe std::vector?
listener = null;
}
}

The listener array consists of a derivation of a listener class.

The OnEvent call may actually end up destroying the AClass object, by a
call to AClass::Destroy().

What I need is a way to end the loop if this function is called, because
the Destroy will invalidate the listener array. In worst case scenarios,
even the AClass object itself might be invalidated. Does anyone have an
idea on how I can accomplish this? I would prefer that the creator of the
listener classes did not have to consider this.

You need to somehow manually detect and work around the problem.

After the the destructor of the instance has been called, any access
to any data member would be illegal, and cause undefined behavior.
I assume that Destroy() is being called from the destructor, or
are you blindly porting code from another language?

One approach would be to use reference counting to manage the lifetime
of the object. Upon entry, ThrowEvent can increment the reference count.
Then during the loop, if the count has gone back down to one, the loop
can exit immediately and proceed with releasing the object.

Another approach is to document this restriction, and to throw an
exception (or otherwise fail or terminate the program) if a call
to Destroy() is attempted while in ThrowEvent.


Similar issues will also occur when an item in the 'listener' collection
is removed while executing ThrowEvent(). The loop index within ThrowEvent()
may need to be somehow adjusted, or the item removal needs to be postponed
(for example by setting an entry to NULL and letting ThrowEvent() do
the clean-up).

I hope this helps,
Ivan
 
M

Morten Aune Lyrstad

Ivan said:
I think you should be using some collection of the standard
C++ library -- maybe std::vector?
I do, this was just an illustration. :)
You need to somehow manually detect and work around the problem.

After the the destructor of the instance has been called, any access
to any data member would be illegal, and cause undefined behavior.
I assume that Destroy() is being called from the destructor, or
are you blindly porting code from another language?
No, everything is created by me. Destroy was called from the destructor,
yes, but it was also supposed to be allowed to be called by itself, in
cases where I want to destroy the contents before the lifetime of the
object ended - that is, i was not using dynamically allocated objects. I
will do so now in my revision.
One approach would be to use reference counting to manage the lifetime
of the object. Upon entry, ThrowEvent can increment the reference count.
Then during the loop, if the count has gone back down to one, the loop
can exit immediately and proceed with releasing the object.
You are the man. You are definitely the man. I am already developing a
reference counted version, but I had a far more complicated solution of
ThrowEvent in mind. Once again, I'm corrected by far superior
programmers. Thank you very much!
Another approach is to document this restriction, and to throw an
exception (or otherwise fail or terminate the program) if a call
to Destroy() is attempted while in ThrowEvent.


Similar issues will also occur when an item in the 'listener' collection
is removed while executing ThrowEvent(). The loop index within ThrowEvent()
may need to be somehow adjusted, or the item removal needs to be postponed
(for example by setting an entry to NULL and letting ThrowEvent() do
the clean-up).
Thank you again, I had not thought of that either. :)
I hope this helps,
Ivan
You bet it does!

Yours,
Morten Aune Lyrstad
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top