3

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?

CodeBricks
  • 1,771
  • 3
  • 17
  • 37
  • 1
    Try instead `shared_ptr sp4 = sp3;`. You need to copy the shared pointer object itself; creating a new shared pointer with the same raw pointer only means now you have two shared pointers that each think they have exclusive ownership of the object. This is easier if you instead do `shared_ptr sp3(new int(8));` -- don't let the raw pointer be visible in your program to avoid accidentally using it. – cdhowie Nov 05 '14 at 22:10
  • 1
    Actually, even better would be `auto sp3 = std::make_shared(8);` as this allows the C++ implementation to allocate room for both the `int` and the `shared_ptr` control block in a single memory allocation. (The standard does not require this optimization, but it does permit it. If you use `shared_ptr(new int(8))` then this optimization is not possible.) – cdhowie Nov 05 '14 at 22:17
  • @cdhowie , Good point. My comment here is just to make it clearer for others that if doing `shared_ptr sp3(new int(8)); shared_ptr sp4(new int(8));`, `sp3` and `sp4` point to different `int`s (at different addresses) that just have the same value. – CodeBricks Nov 05 '14 at 22:22

2 Answers2

6

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.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • So is other `shared_ptr`s' copy-construction and assignment the *only* ways to increase `use_count`? – CodeBricks Nov 05 '14 at 22:11
  • 1
    @CodeBricks Yes. Otherwise there would have to be some global table of raw pointers so that when you construct a brand new instance from a raw pointer it could go look up the reference count object, and that would require synchronization to the global table in a multithreaded program... it would be a nightmare. – cdhowie Nov 05 '14 at 22:12
  • @codeBricks: The only ways that don't trigger undefined behavior, yeah. (There are a bunch of member functions hanging off `shared_ptr` that may change this; I'm not completely positive for everything; but any other members behave like copy construction or copy assignment in some way) – Billy ONeal Nov 05 '14 at 22:14
1

You should use a copy constructor of shared_ptr to construct the second shared pointer properly.

kraskevich
  • 18,368
  • 4
  • 33
  • 45