Managing pointers upon object destruction in C++

Started by
20 comments, last by Irusan, son of Arusan 5 years, 2 months ago

Good Evening,

I have a question that's been a constant source of frustration and I need to learn to deal with it.

Say I have a warrior with a memory (AI) module that polls with world through it's vision and it 'remember's' through a pointer (pointing to a troll) that a troll was detected 2000ms ago.

Now, say I have a wizard that collided with the same troll, and the wizard now has a pointer to the troll 'remembering' the last thing it collided with.

Somewhere down the road, there's a delete call on the troll object.

Now I have the memory module and the wizard with pointers to a deleted object. Let's multiply this over hundreds or thousands of objects and it becomes insanely complex with a multitude of objects and various pointers pointing to objects that may or may not be deleted.

So my questions is this.

Is there a design pattern (hopefully not too obtrusive), algorithm, or a way upon object destruction to notify every other object that's pointing to the destroyed object that it's been deleted so i can NULL the pointers?

Thanks in advance for your time and answers.

Mike

Advertisement

You can Reference Count (RefCount) pointers to your objects, which would prevent them from being deleted in the first place. Only when nobody points to an object (RefCount==0), you actually destroy it. You do this by only ever calling Release() on your objects and never the destructor directly (Release will call it).

Another option would be weak pointers (C++ standard weak_ptr + unique_ptr). With a weak pointer, you can first simply test whether it's nullptr before dereferencing it.

Another option would be to remember just a unique handle (GUID) and every time you're trying to access info about an object, you'd ask a manager (GlobalTrollManager.GetTroll(warrior.lastCollidedTrollID)) and test for nullity, instead of directly accessing the troll (*warrior.lastCollidedTrollPtr). The unique IDs would never recycle, every new object would get a completely fresh one. The manager, if it doesn't find the ID in the troll list, would simply return nullptr ("I don't have such a troll at all").

In all cases, you should be thread-aware, make sure you don't have any race conditions.

How about something like this.........  Each relevant player and/or NPC has a smart pointer referencing the Troll.  And you also have a smart pointer reference which represents the Troll itself somewhere in a list of your MOBs.  When the Troll dies it's pointer goes away, and it has a flag which sets it to dead.   Now when the other NPCs or players that are referencing it try to access it, they find out it's gone and simply set their smart pointers to null too. Also If they are set to a different MOB, the same thing happens. So the actual memory for the Troll stays around but it's flagged to dead. When the Troll is no longer being referenced by anything, it's collected by the smart pointer system.

The common design pattern to use with such a scheme is called Observer. It is depicted in the Gof book:

Quote

Define a one-to-many dependancy between objects so that when one object changes state, all its dependents are notified and updated automatically

With templates you can achieve something not too intrusive, easily and fully reusable everywhere you would need it.

Smart pointers are also a good choice. But be careful with shared_ptr if you want to go for them, they are not the fastest thing. You can achieve to make your owns which will be far more fast.

Quote

Another option would be weak pointers (C++ standard weak_ptr + unique_ptr)

 

@pcmaster - I was reading about the weak_ptr option yesterday but a number of sources wrote that's not available for unique_ptr only the shared_ptr.

Is my understanding incorrect?

 

One more thing. If you are worried about speed with std::shared_ptr, you simply don't have to use it. You can put an integer in your class somewhere, and make a pointer class to your it.  If you aren't using threading in that part of the code, don't even make it atomic. std::shared_ptr has a reputation of being slow.  In some cases it allocates the counter and the actual object, in a separate memory blocks, so that means you have a whole second level of de-referencing, and even where you don't, the code may have to check for it. I never used it because it's hard to determine what it's actually doing. You can make make some simple reference counting in a few minutes without it and it's pretty simple. 

Also whether you use std::shared_ptr or not, don't use any smart pointers everywhere, only where ownership needs to be represented.  You don't need to pass them around when you know ownership will not change and you are just accessing your data. Basically you typically never pass smart pointers in and out of working functions, they are just members of other class objects or are in containers.  There may be exceptions but if you are passing them around a lot you are probably doing something wrong.

1 hour ago, too_many_stars said:

@pcmaster - I was reading about the weak_ptr option yesterday but a number of sources wrote that's not available for unique_ptr only the shared_ptr.

Is my understanding incorrect?

You are correct.  The std::weak_ptr implements the observer pattern on a std::shared_ptr.  It's the idiomatic C++ way to do what you're asking, and exactly why the std::weak_ptr/std::shared_ptr was invented.

Stephen M. Webb
Professional Free Software Developer

2 hours ago, too_many_stars said:

 

@pcmaster - I was reading about the weak_ptr option yesterday but a number of sources wrote that's not available for unique_ptr only the shared_ptr.

Is my understanding incorrect?

 

That is correct.  Weak_ptr is there to work with shared_ptr.  A unique_ptr is supposed to be unique and it makes no sense to have a weak_ptr that locks into another unique_ptr.

In any case, it sounds like you need to use shared_ptr + weak_ptr.  They are there specifically for the type of situation that you describe.

Pointers considered harmful. Handles are the better pointers. ?

https://floooh.github.io/2018/06/17/handles-vs-pointers.html

Weak pointers, and/or ref counting are overkill. Even raw pointers on 64bit platforms are overkill. Integer-based handles actually have a lot of advantages over pointers ?

This topic is closed to new replies.

Advertisement