It's not the first time I am getting the RuntimeError: underlying C/C++ object has been deleted
. I've solved it many times altering my code in a random but intuitive way but now I am facing this again and just don't understand why it happens...
What I ask for is a generic approach to confront and solve this error.
I will not post code samples here because my project is too complex and I just can't figure out where's the bug. And also because I am asking for the universal solution not just for this case.
Why can the 'underlying C/C++' objects be deleted?
How to avoid it?
How to test if the underlying object exists?
You cannot test if the underlying object exists (e.g., if it has been "deleted"). There is some "trickery" you can employ to "kind-of" help with this, but I don't recommend it. Rather, IMHO, I'd guess your design lacks strong ownership semantics. (That may be necessary depending on the complexity of the problem and your domain, but I'd recommend against it, if it is avoidable.)
Your problem is ownership semantics. Most especially in C++, as opposed to other languages that employ memory "garbage collection", you MUST have a design where you have a strong understanding of "who owns what". Your design should make it "easy and obvious" to know what objects exist, and who "owns" them.
Generally, this means to centralize the "creation" and "deletion" of objects into a "parent" of some kind, where there is never any question as to, "Whose object is this?" and "What are your objects that you manage?"
For example, a "Parent" class will allocate member objects that it "needs", and only that parent deletes those objects. The parent's destructor ensures those members are deleted, so those objects don't accidentally get "left around". Thus, the parent "knows" its member objects, and when the parent is gone, you KNOW the member objects also are gone. If your design is strongly coupled, then the member objects may reference the parent object, but that is the "deep end of the pool" for ownership semantics (and usually not recommended).
Some designs can be legitimately very complicated. However, in general, ownership semantics should NEVER be complicated: You should ALWAYS know "who owns what", and thus, you should ALWAYS know "what objects exist".
Other designs are possible with garbage collection languages, where objects may be "free agents" and you trust the garbage collector to do the "hard work" of cleaning up objects about which you've lost track. (I'm not partial to those designs, but concede them to be sometimes acceptable in those other languages, based on unique problem domains.)
If your C++ design relies upon such "free agents" objects, you can write your own garbage collector, or own "utility owner" to clean them up. For example, the MyFreeAgent
constructor could register itself with the MyGarbageCollector
, and the MyFreeAgent
destructor would unregister itself from the MyGarbageCollector
. Thus, MyFreeAgent
instances could be "cleaned up" by the MyGarbageCollector
, or the MyFreeAgent
could even kill itself with "delete this;
", and you would KNOW that it is gone (because its destructor would unregister itself from the MyGarbageCollector
). In this design, a single MyGarbagageCollector
would ALWAYS "know" what MyFreeAgent
instances exist.
Such designs are the "deep end of the pool" and should be used with extreme caution, even though they work, because they have the strong potential to massively de-structure your system. (I've used them, but you've got to be sure the problem honestly warrants such a design.)