Reputation: 109
I'm trying to get an idea on how to use std::shared_ptr
in C++. But it's pretty confusing and I don't understand how to create multiple shared pointers pointing to the same object. Even the documentation and online material is not very lucid.
Following is a small piece of code I wrote to try and understand std::shared_ptr
's behavior:
#include <iostream>
#include <memory>
using namespace std;
class Node
{
public:
int key;
Node()
{
key = 0;
}
Node(int k)
{
key = k;
}
};
int main()
{
Node node = Node(10);
shared_ptr<Node> ptr1((shared_ptr<Node>)&node);
cout << "Use Count: " << ptr1.use_count() << endl;
// shared_ptr<Node> ptr2=make_shared<Node>(node);//This doesn't increase use_count
shared_ptr<Node> ptr2((shared_ptr<Node>)&node);
cout << "Use Count: " << ptr2.use_count() << endl;
if (ptr1 == ptr2)
cout << "ptr1 & ptr2 point to the same object!" << endl;
if (ptr1.get() == ptr2.get())
cout << "ptr1 & ptr2 point to the same address!" << endl;
cout << "ptr1: " << ptr1 << " "
<< "ptr2: " << ptr2 << endl;
return 0;
}
Based on the output I got, both ptr1
and ptr2
pointed to the same Node
object but the use_count
is 1 for both of them. Even using std::make_shared
doesn't work and the program crashes before exiting.
Can you please tell me what I'm doing wrong? And how to create multiple shared_ptr
(s) that are pointing to the same object.
Upvotes: 4
Views: 1994
Reputation: 1255
there are multiple missunderstandings, but i will try to explain: in c++ objects are either stored on the stack or the heap. objects created on the stack will get destroyed and their memory gets released as soon as you leave the scope in which they are declared. this happens full automatically for you. for example:
int bla()
{
int a = 1;
int b = 2;
int result = a+b;
return result;
}
in this example the 3 int
objects will get created when the programm enter the function bla
and will get destroyed when the function returns accordingly.
then you have the heap. you can create objects on the heap via new
. this objects outlive the scope in which they got created, but you have to remember to destroy the objects via delete
. because this is a common pitfall and source for memoryleaks the standard library provides helper classes (for example the shared_ptr
which you already found) to overcome this issue. the idea is, that the shared_ptr
object will take the responsibility to delete the given object (which has to be on the heap(!) (yeah you can bypass this by a custom deleter, but thats a little bit more advanced than this explanation)) as soon as the shared_ptr itself gets destroyed. like the name suggests, the shared_ptr
's intend is to be shared. to be shared you just copy the shared_ptr.
the shared_ptr
implementation has a custom copy constructor and copy assignment operator which will handle this by incrementing the use count and copying the address of the object that is currently handled. and this is the only way how a shared_ptr object will get to know wether there are other instances managing the same object or not. so to increase the use count you have to make a copy of the shared_ptr
.
now let me explain what went wrong:
your initial Node node = Node(10);
is created on the stack (like the ints above). so there is no need to manage the lifetime manually. you created a shared_ptr
, managing the address of this node. that is a misstake. as soon as you leave the scope the shared_ptr
AND the node object itself(through the automatism on the stack) are going to delete the node object. and thats bad.
lets imagine error 1 did not exists. so we have a second misstake:
you are creating a second, seperate shared_ptr
instead of copying from the first one. now both existing shared_ptr
dont know about each other and each of them will try to delete the node object. so we have again a double delete.
lets pretend you want your Node object really be managed by a shared_ptr
, you have to start with one shared_ptr and later copy from it as much as you want to share it. to create the first one you have 2 possiblilties:
std::shared_ptr<int> mysharedpointer (new int(ANYNUMBER));
auto mysharedpointer = std::make_shared<int>(ANYNUMBER);
usually you would prefer the second one, since it comes with a slight perfomance advantage and more safety when used in a function call context.
if you now want a second shared_ptr
, sharing ownership with the first, you can do this by copying the first pointer:
auto mysecondsharedpointer = mysharedpointer;
and voila you have a copy associated with the shared ownership.
(this is all a bit simplified and i am sure i missed something. feel free to complete the answer)
Upvotes: 2
Reputation: 173024
The use_count won't increase when you create shared_ptr
s separately (including using make_shared
), they're not shared at all. The created shared_ptr
knows nothing about other shared_ptr
s and the pointers being managed, even the pointers might happen to be the same (note that it might lead to multiple destruction).
You need to tell which shared_ptr
s should be shared; the use_count increases when you create shared_ptr
from another shared_ptr
. e.g.
shared_ptr<Node> ptr1 = make_shared<Node>(10);
cout<<"Use Count: "<<ptr1.use_count()<<endl; // 1
shared_ptr<Node> ptr2(ptr1);
cout<<"Use Count: "<<ptr2.use_count()<<endl; // 2
cout<<"Use Count: "<<ptr1.use_count()<<endl; // 2
BTW: As the comments suggusted, it's dangerous to make a shared_ptr
managing a pointer to &node
, which points to an object allocated on stack. The following code might match your intent closely.
Node* node = new Node(10);
shared_ptr<Node> ptr1(node);
cout<<"Use Count: "<<ptr1.use_count()<<endl; // 1
shared_ptr<Node> ptr2(ptr1);
cout<<"Use Count: "<<ptr2.use_count()<<endl; // 2
cout<<"Use Count: "<<ptr1.use_count()<<endl; // 2
Upvotes: 7
Reputation: 510
shared_ptr<>
s own the object they are pointing too. That means you can't create a shared_ptr from a stack object. To create a shared_ptr use new
or make_shared
:
shared_ptr<Node> ptr1(new Node(42));
shared_ptr<Node> ptr2 = make_shared<Node>();
shared_ptr<Node> ptr3 = make_shared<Node>(99);
These are all separate objects and all have use_count()==1
.
If you want to copy them just do so:
shared_ptr<Node> copy1 = ptr1;
Now ptr1
and copy1
have use_count()==2
.
Upvotes: 4