Search code examples
c++pointersdangling-pointer

Dangling pointer issue C++


I'm making a game engine and I have run into a problem with destroying elements.

For example I have some cameras that follow and look at an target (a gameobject or an actor). Later in the game the camera target is destroyed and the camera code will crash because the code will try to access to a freed object and I want to avoid this crash.

So here is a simplified code example:
I have an instantiated object, an int for example, to keep things simple. I have several references to this object. If the instantiated object is deleted, I'd like to be able to check if the object still exists or not if one of my scripts wants to access to the destroyed object.

int* refToMyInt = nullptr; //I want to possibility to have empty pointer
int* refToMyInt2 = nullptr;

int* myInt = new int(2);
refToMyInt = myInt;
refToMyInt2 = refToMyInt;

delete myInt;

if(refToMyInt == nullptr && refToMyInt2 == nullptr)
  std::cout << "myInt Deleted!" << std::endl; // Never called

Except that it doesn't work, delete doesn't seem to change the variables to nullptr.
I don't want to manually set refToMyInt and refToMyInt2 to nullptr because I could have dozens of references to that object.
I've found a solution but it was to use weak_ptr and I'd like to avoid using it because using the lock() function to access the values each time is a bit cumbersome and impractical...

Is there another solution? Thank you!


Solution

  • "I'd like to avoid using it because using the lock() function to access the values each time is a bit cumbersome and impractical."

    This might be true for *(ptr.lock()) vs *ptr but this is a false comparison. You should always check if ptr is a nullptr before dereferencing it:

    if (ptr != nullptr) {
      auto value = *ptr;
    }
    

    and that is only marginally more verbose than:

    if (auto sptr = ptr.lock()) {
      auto value = *sptr;
    }
    

    Answer:

    The solution is a combination of std::shared_ptr and std::weak_ptr.

    Instead of:

    int* refToMyInt = nullptr; //I want to possibility to have empty pointer
    int* refToMyInt2 = nullptr;
    
    int* myInt = new int(2);
    refToMyInt = myInt;
    refToMyInt2 = refToMyInt;
    
    delete myInt;
    
    if(refToMyInt == nullptr && refToMyInt2 == nullptr)
      std::cout << "myInt Deleted!" << std::endl; // Never called
    

    you can write:

    std::weak_ptr<int> refToMyInt;
    std::weak_ptr<int> refToMyInt2;
    
    std::shared_ptr<int> myInt{ new int(2) };
    
    refToMyInt = std::weak_ptr<int>(myInt);
    refToMyInt2 = std::weak_ptr<int>(myInt);
    
    myInt.reset();
    
    if(refToMyInt.expired() && refToMyInt2.expired())
      std::cout << "myInt Deleted!" << std::endl; // Never called
    

    I kept the original variable names to make comparisons easier.