Reputation: 39
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!
Upvotes: -2
Views: 149
Reputation: 340
"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.
Upvotes: 0
Reputation: 13076
These examples will show you a bit more of the intended use of vector/make_unique.
#include <memory>
#include <iostream>
#include <vector>
// dynamic memory allocation is mostly only necessary
// for polymorphic classes (e.g. classes derived from
// an abstract baseclass)
class my_interface
{
public:
virtual void do_something() = 0;
virtual ~my_interface() = default;
protected:
my_interface() = default;
};
//
class my_concrete_class :
public my_interface
{
public:
my_concrete_class() = default;
// to show my_concrete_class
// instance will be deleted by smart pointer.
~my_concrete_class()
{
std::cout << "my_concrete_class destructor\n";
}
void do_something() override
{
}
};
int main()
{
int my_int{ 1 };
int& ref_to_my_int{ my_int }; // no & this ensure a ref is always refering to a valid object
int* ptr_to_my_int{ &my_int }; // non-owning pointer https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#r3-a-raw-pointer-a-t-is-non-owning
ref_to_my_int = 42; // modify through reference
std::cout << "my_int now has value " << my_int;
// create a scope that determines life cycle of my_concrete_class.
{
std::cout << "entering scope 1\n";
std::unique_ptr<my_interface> itf_ptr{ std::make_unique<my_concrete_class>() };
std::cout << "going out of scope , unique_ptr will be destructed\n";
}
{
std::vector<int> values{ 1,2,3 }; // will dynamically allocate memory for 3 ints
std::cout << "vector going out of scope, will delete allocated memory\n";
}
return 0;
}
Upvotes: -1