This is a simplification of an issue I encountered in another project.
Say I have the following class:
class MyClass {
public:
MyClass() {
std::cout << "MyClass constructed\n";
Instances().insert(this);
}
~MyClass() {
std::cout << "MyClass destructed\n";
Instances().erase(this);
}
static std::unordered_set<MyClass*>& Instances() {
static std::unordered_set<MyClass*> _instances;
return _instances;
}
};
It has a static unordered_set
that it uses for keeping track of existing instances of the class. When an instance is constructed, its address is added to the set; when an instance is destroyed, its address is removed from the set.
Now, I have another class that has a vector
of shared_ptr
s containing instances of MyClass
:
struct InstanceContainer {
std::vector<std::shared_ptr<MyClass>> instances;
};
A key point here is that there is a global instance of this class above main
. This seems to be part of the problem, because declaring the class inside of main
does not produce the issue.
Inside of main
, I do the following (say the global instance of InstanceContainer
is called container
):
container.instances.emplace_back(std::shared_ptr<MyClass>(new MyClass));
Everything is fine until the program terminates, when I get a read access violation ("vector subscript out of range") when Instances().erase(this)
is executed in MyClass
's destructor.
I thought that maybe I was attempting to erase the instance from _instances
multiple times (hence the cout
s)-- However, the contructor is only called once, and the destructor is only called once, as you'd expect. I found that when this happens, _instances.size()
is equal to 0
. The weird thing is, it's equal to 0
before any calls to erase
. Prior to anything being erased from the set, it's empty?!
My theory at this point is that this has to do with the order in which the objects are destructed as the program terminates. Perhaps the static _instances
is being freed before the destructor for MyClass
is called.
I was hoping someone would be able to shed some light on this, and confirm whether or not that's what's happening.
My workaround now to check to see if _instances.size()
is 0
before attempting to erase. Is this safe? If not, what else can I do?
Aucun commentaire:
Enregistrer un commentaire