Code Design: Object deleted before higher code is completely finished with it

Started by
13 comments, last by Kylotan 14 years, 1 month ago
I've been running into this design problem for a while, and it has been bugging me. I am posting it here in the hope that it will finally be resolved.

CClass *objects[MAX_OBJECTS];

void Foo()
{
	for (int i = 0; i < MAX_OBJECTS; i++)
	{
		if (objects != NULL)
		{
			Bar(i);
			objects->A();
		}
	}
}

At some point in Bar(), the object #i may be deleted. If this happens, everything after Bar() that tries to access object #i will fail. I know multiple ways that this can be solved. However, because I am relatively new to code design and OOP, I don't know if there is a standard/common/best solution. I could choose one of my solutions at random, but I would rather learn what the "proper" solution is, if such a thing exists. I have not posted my own solutions here because I don't want to accidentally influence responses. If requested, I can provide my thoughts on the matter. Please note that I am looking for an abstract solution. If the solution for this problem is dependent on the exact situation, please let me know and I will give a specific situation in which this problem is occurring.
Advertisement
Rather than deleting the object directly within Bar(), consider adding it to a 'destroy list'. At the end of Foo(), you can go through the destroy list and delete all the objects therein - by that point, nobody else will need them.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Thank you for the quick reply.

Please don't consider this topic "solved" just yet though. I am hoping for multiple responses, and possibly a discussion of the issue.
The general-purpose design pattern is a deferred processing queue.

Maintain a list of callback functions to be executed when everything else is done. In cases like this, where you have work to be done at some later time, add it to the list. A good time to run the list is at the end of every frame in your main loop.



A "delete me later" list is a special case of the general pattern. It may be the easier solution if you are only doing the one case.

Quote:Original post by frob
A "delete me later" list is a special case of the general pattern. It may be the easier solution if you are only doing the one case.
The issue is that even if you have the general deferred processing queue, you may *still* need the 'delete me later' list, because some deferred callbacks may depend on the item to be deleted (and it isn't trivial in the general case to figure out which).

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by VBStrider
At some point in Bar(), the object #i may be deleted. If this happens, everything after Bar() that tries to access object #i will fail.

No, it won't fail. It will do something undefined, and if state isn't modified too much, will even succeed.

Invalid memory access in C++ is merely undefined, and never an error.

First rule: Whoever allocates, also de-allocates. This solves the problem completely. Bar() isn't allowed to de-allocate.

For everything else, either use managed references or value types.

Also note that your example doesn't show who allocates.


Quote:but I would rather learn what the "proper" solution is

"Proper"?

void Foo(vector<CClass> & entities);void Baz(CClass & entity);

Neither of them deals with resource management in any way.

Quote:may be deleted
What exactly does 'deleted' mean? That delete is called on an instance? That specific instance is no longer needed? That it must be shredded right here and right now?

Or - does it mean that that particular instance is no longer part of objects?

If last, then the problem has to deal with single responsibility. Baz becomes redundant then, and Foo needs to take care of both, since only Foo knows about container, Baz only knows about individual instance.


Quote:Please note that I am looking for an abstract solution.
Before looking for a solution, first define the actual result you wish to accomplish. Not a for loop, or array, but "I have a list of all loaded sprites which are automatically loaded when needed" or similar.
Why is the object allowed to be "deleted" (what does that mean?)
"Deleted" means that the object is invalid, no longer usable, doesn't exist, etc. The exact meaning depends on the solution used (delayed deletion, immediate delete and later checks, etc).

Quote:It will do something undefined, and if state isn't modified too much, will even succeed. Invalid memory access in C++ is merely undefined, and never an error.

Which is a failure... When I said "fail", I did not mean to attach any language or platform specifics to it. The word is used to mean "does not work correctly".

Quote:Before looking for a solution, first define the actual result you wish to accomplish. Not a for loop, or array, but "I have a list of all loaded sprites which are automatically loaded when needed" or similar.

One example of where this becomes a problem is in network packet handling. For this example, objects are called clients and Bar() handles network packets for client #i. Among other reasons, an error in a packet could cause the client to be forcibly disconnected (and thus, the client object would be removed). The call to CClass::A() represents any code that would be executed after the client has been removed.

Another example is where Foo() is managing the characters (and possibly other things) in the game. The object is a character. Bar() represents one of possibly multiple character management functions where the character could become removed from the world due to death or other reasons specific to the game. The call to CClass::A() represents any code that would be executed after the character has been removed.
Why are you storing pointers to the objects rather than storing objects directly?

What does it *mean* for the object to be deleted? If 'Bar' concludes that the object should be deleted, do you still want to call A() on it before deleting it?
Quote:Original post by Zahlman
Why are you storing pointers to the objects rather than storing objects directly?

It is the way I decided to write the example code. I could have easily used an array of objects instead, and the problem would still hold.

Quote:Original post by Zahlman
What does it *mean* for the object to be deleted? If 'Bar' concludes that the object should be deleted, do you still want to call A() on it before deleting it?

No, A() should not be called on the object after the object has been deleted. When an object is deleted, it is no longer usable.

This topic is closed to new replies.

Advertisement