Reputation: 347
Hi I am making my own reference counting smart pointer, but before I start there are two concepts I don't quite understand.
I get that when a shared pointer is created to point to an object I must allocate memory for a struct/class that will contain information such as the reference count (initially one) and maybe a mutex as well for incrementing and decrementing. When I use, say the =operator to make another shared pointer also point to this object, I will also pass the pointer to this struct/class to that new pointer so I can increment the count. My question is, if I make a third shared pointer point to this object(not using the copy constructor or =operator), then this pointer won't know about the struct and therefore will have a reference count of 1, if I then delete the pointer, the count will reach 0 and the object will be destroyed when in fact, there are two other pointers for this object?
If a shared pointer has a reference count of 1, and then multiple threads are created, if one thread ends/destroys it, what happens with the other threads that may still be running?
Upvotes: 3
Views: 1131
Reputation: 238461
The answer is yes. If you create a shared pointer from a plain pointer to an object, when the object is already owned by another shared pointer, then the object will be destroyed when one reference count reaches 0 and the remaining shared pointers will be left dangling - they keep pointing where the object used to exist.
This is why the user of a smart pointer must never grant ownership of a pointer from code that itself does not have the ownership.
There is a race condition with checking and decrementing the reference counter. If the a shared pointer may be used in multiple threads, then the access to the reference counter must be synchronized across threads.
Upvotes: 1
Reputation: 181047
My question is, if I make a third shared pointer point to this object(not using the copy constructor or =operator), then this pointer won't know about the struct and therefore will have a reference count of 1, if I then delete the pointer, the count will reach 0 and the object will be destroyed when in fact, there are two other pointers for this object?
There really is nothing you can do about this. If you have a pointer and you give it to two separate shared pointers then you get a double delete.
int * foo = new int(10); // set value to 10
{
std::shared_ptr<int> a(foo);
std::shared_ptr<int> b(foo)
} // uh-oh. we call delete twice
There is no way to mark the pointer as being owned. It is up to the person writting the code to not do this. The way we get around things like this is to either create the pointer directly in the constructor so you cannot give it to another instance
std::shared_ptr<int> a(new int(10));
or you can use std::make_shared
auto b = std::make_shared<int>(10);
Now we no longer have to worry about giving the raw pointer to another shared pointer and having it deleted out from under us.
If a shared pointer has a reference count of 1, and then multiple threads are created, if one thread ends/destroys it, what happens with the other threads that may still be running?
If you pass the shared pointer to each thread then all of them share in the ownership. The pointer will not be deleted until all of the threads end and the original shared pointer goes out of scope.
Upvotes: 1
Reputation: 20396
In a typical implementation, if you create a shared_pointer
to an object, and create a second shared_pointer
to the same object without referencing the already created shared_pointer
, they'll both have a reference count of 1, delete the object when either goes out-of-scope, and cause undefined behavior when the other tries to delete the object.
This is [one of] the main reason that the STL has factory objects like std::make_shared
and std::make_unique
, as ensuring that the raw object pointer is never managed by the programmer helps prevent these kinds of mistakes.
Of course, a smarter implementation might try to track on a static level all the pointers that have been created & maintained, and may try to memoize the objects. But that comes with its own risks and issues, so I won't advise that.
As to the multithreading issue: it depends. If all the threads are creating shared_pointer
based on an original shared_pointer
object, and you're using your mutex
correctly to maintain the reference count, then they should all increment the reference count properly without issue, and the object shouldn't get deleted just because a single thread stopped using it.
Upvotes: -1
Reputation: 264719
I get that when a shared pointer is created to point to an object I must allocate memory for a struct/class that will contain information such as the reference count (initially one) and maybe a mutex as well for incrementing and decrementing.
Yes you will need a counter.
The mutex is only required if you plan on multithreading. I would concentrate on getting the counting working first worry about the locking afterwords.
When I use, say the =operator to make another shared pointer also point to this object, I will also pass the pointer to this struct/class to that new pointer so I can increment the count.
The point about shared pointer is that they take ownership of the pointer. Once you have created the shared pointer there should be no instances of a RAW pointer to the same object. So when you copy or assign you are doing it all through the shared pointer.
My question is, if I make a third shared pointer point to this object(not using the copy constructor or =operator), then this pointer won't know about the struct and therefore will have a reference count of 1.
Your assumption is correct. That is why when you create the shared pointer you should not keep a copy of the pointer. This is another reason why std::make_shared()
was introduced, it allocates memory and immediately wraps it in a smart pointer thus no RAW pointer is returned to user code.
if I then delete the pointer, the count will reach 0 and the object will be destroyed when in fact, there are two other pointers for this object?
That is exactly the problem. This is why you should not create or pass around RAW pointers to objects that are already being managed.
If a shared pointer has a reference count of 1, and then multiple threads are created, if one thread ends/destroys it, what happens with the other threads that may still be running?
If your reference count is working and you only have shared pointers managing the RAW pointer it should work as expected. But if you delete it in one thread it is destroyed in all threads.
Upvotes: 4
Reputation: 136515
My question is, if I make a third shared pointer point to this object(not using the copy constructor or =operator)
Since you are bypassing the mechanism to copy the shared pointer this is going to be undefined behaviour. Use copy constructor or assignment operator.
If a shared pointer has a reference count of 1, and then multiple threads are created, if one thread ends/destroys it, what happens with the other threads that may still be running?
That implies that you are using a shared pointer after its destructor has completed, which is undefined behaviour. Make sure those threads have their own copies of the shared pointer to avoid this case, or make sure that the pointer does not get destroyed while other threads use it.
Upvotes: 1