Search code examples
c++pointersmemory-managementshared-ptrdouble-free

What are all the ways use_count of std::shared_ptr is incremented?


I have two shared_ptrs pointing to the same int, i.e. calling get() on them returns the same address. But calling use_count() on them returns 1. When the last of them goes out of scope, it tries to free up the memory already freed by the other one, causing a double free runtime error:

#include <memory>
#include <iostream>
using namespace std;

int main() {
    shared_ptr<int> sp1(make_shared<int>(7));
    shared_ptr<int> sp2(&(*sp1));
    cout << sp1.use_count() << endl;  // 1
    cout << sp2.use_count() << endl;  // 1
    cout << sp1.get() << endl;        // same address
    cout << sp2.get() << endl;        // same address
}
// ^ Double free runtime error at closing brace.

Same thing happens in this variant with an explicitly declared raw pointer:

int main() {
    int *raw_ptr = new int(8);
    shared_ptr<int> sp3(raw_ptr);
    shared_ptr<int> sp4(raw_ptr);
    cout << sp3.use_count() << endl;  // 1
    cout << sp4.use_count() << endl;  // 1
    cout << sp3.get() << endl;        // same address
    cout << sp4.get() << endl;        // same address
}
// ^ Double free runtime error at closing brace.

Why does use_count() return 1 (but not 2) if both shared_ptrs point to the same thing? If use_count() returned 1, then why was there the attempt to free the int twice? I thought that a shared_ptr will increase use_count by one if and only if it points to the same address as its brethren shared_ptrs.

Is a std::shared_ptr’s use_count incremented only by the first shared_ptr's construction by raw pointer (or assignment to raw pointer, if default-constructed) and then by additional shared_ptrs’ copy-construction or assignment by any of the prior shared_ptrs? And what are all the other ways of incrementing, if any?


Solution

  • When you give a pointer to a shared_ptr, that shared pointer takes ownership of that pointer. You are no longer allowed to delete it, pass it to another shared_ptr, or similar. There's no global lookup table for the new shared_ptr to check to find that there's already another shared_ptr that already owns the pointer or anything like that.

    Put another way, the double free error occurred when the second shared_ptr was created by passing in the same pointer rather than copying the shared_ptr itself (which increments the use count). The fact that you can't observe the undefined behavior until later doesn't change where it actually occurs.