How can memory leaks occur when using std::shared_ptr?
sobes.tech AI
Answer from AI
Memory leaks when using std::shared_ptr are most often caused by cyclic references. When two or more std::shared_ptr objects refer to each other, their reference counts can remain non-zero even if there are no external references to these objects. This prevents the objects from being destroyed and the associated memory from being freed.
Example of a cyclic reference:
struct Foo {
std::shared_ptr<Foo> other;
};
auto p1 = std::make_shared<Foo>();
auto p2 = std::make_shared<Foo>();
p1->other = p2; // p1 refers to p2
p2->other = p1; // p2 refers to p1
// After p1 and p2 go out of scope, their reference counts
// will remain at 1 due to mutual references, preventing deallocation.
To prevent such leaks, std::weak_ptr is used. std::weak_ptr holds a non-owning reference to an object managed by std::shared_ptr. It does not increase the reference count.
Example of preventing cyclic references with std::weak_ptr:
struct Foo {
std::weak_ptr<Foo> other; // Using weak_ptr
};
auto p1 = std::make_shared<Foo>();
auto p2 = std::make_shared<Foo>();
p1->other = p2;
p2->other = p1;
// When p1 and p2 go out of scope, their shared_ptrs are destroyed,
// reference counts become zero, and memory is freed.
// weak_ptr does not prevent this.
Another, less common way to cause memory leaks with std::shared_ptr is by using custom deleters that contain errors or do not perform their task properly.
// Faulty deleter that does nothing
void faulty_deleter(int* ptr) {
// delete ptr is not called
}
auto p = std::shared_ptr<int>(new int(10), faulty_deleter);
// Memory allocated for int will not be freed when p is destroyed.
Using exceptions during the creation of std::shared_ptr can lead to loss of ownership over raw pointers if they were created before initializing the shared_ptr. It is recommended to use std::make_shared or std::allocate_shared for atomic allocation.
int* raw_ptr = new int(5);
// If an exception occurs here before creating the shared_ptr
// raw_ptr memory will be lost
// std::shared_ptr<int> sp(raw_ptr); // Creating shared_ptr