Reputation: 919
Ran into this bug where a class contains a pointer to itself -- and it was pointing to the same instance. Here is the smallest code snippet that contains that problem...
class Node
{
public:
Node()
{
std::cout << "constructor" << std::endl;
}
~Node()
{
std::cout << "destructor" << std::endl;
}
void storeChild(boost::shared_ptr<Node> ptr)
{
m_ptr = ptr;
}
private:
boost::shared_ptr<Node> m_ptr;
};
int main()
{
boost::shared_ptr<Node> bc_ptr(new Node());
bc_ptr->storeChild(bc_ptr); // this line is the bug
return 0;
}
My questions are:
1. Is the Node object ever deleted? I know smart pointers are supposed to manage those things so we don't have to delete. But it looks like the reference that's passed into the class using storeChild
never is reset. Does this mean I'm going to have a memory leak?
2. Is there a way to use smart pointers that prevents this from happening? Clearly the storeChild
method should be given a pointer to a different Node but how do you prevent that?
If I run this program, the destructor never gets called.
Upvotes: 0
Views: 871
Reputation: 117298
Yes, this is a memory leak since the shared pointer in Node
never will be deleted. Consider:
#include <iostream>
#include <memory>
class Node {
std::shared_ptr<Node> m_ptr;
public:
Node() { std::cout << "constructor\n"; }
~Node() { std::cout << "destructor\n"; }
void storeChild(std::shared_ptr<Node> ptr) { m_ptr = ptr; }
long use_count() { return m_ptr.use_count(); }
};
int main() {
Node* n = new Node;
{
std::shared_ptr<Node> bc_ptr(n);
bc_ptr->storeChild(bc_ptr); // this line is the bug
std::cout << n->use_count() << "\n"; // prints 2
}
std::cout << n->use_count() << "\n"; // prints 1
// the Node pointed to by n is leaked
}
It's probably even more obvious if you do like this:
#include <iostream>
#include <memory>
class Node {
std::shared_ptr<Node> m_ptr;
public:
Node() : m_ptr(this) { std::cout << "constructor\n"; }
~Node() { std::cout << "destructor\n"; }
};
int main() {
new Node;
}
If you try:
int main() {
delete new Node;
}
The Node
destructor will be called twice. First by your delete
and secondly by the shared_ptr
when it's deleted.
Preventing it can be tricky if it's possible to end up with circular ownership, like: A->B->C->A ... You should probably use std::weak_ptr
if that's a risk.
Upvotes: 1