I just stumbled on the boost::shared_ptr documentation, which goes:
Sometimes it is necessary to obtain a shared_ptr given a raw pointer
to an object that is already managed by another shared_ptr instance.
Example:
void f(X * p)
{
shared_ptr<X> px(???);
}
Inside f, we'd like to create a shared_ptr to *p.
In the general case, this problem has no solution.
Why? Is it not allowed to do something like:
shared_ptr<X> px(p);
Am I missing something?
If you have a shared_ptr managing a pointer and then create another shared_ptr managing the same pointer (NOT copying the original shared_ptr), you end up with two managers for the same resource. When one of the two reach a reference count of 0, it will delete the object and the other shared_ptr will point to deleted memory with all that follows.
If you would do this:
main() {
Object *obj = new Object();
func(obj)
}
void func( Object *obj ) {
shared_ptr objPtr(obj); // take ownership.
objPtr->fun();
// Passed object "obj" is destroyed here.
}
At the end of the function func the object pointer would get destroyed, and with the pointer the object itself. This wouldn't be a desirable behaviour.
Actually, you can do that, but you must know that the pointed object will be deleted when exiting the function...
I tried with boost:
void f(X * p)
{
boost::shared_ptr<X> px(p);
// do smething
}
void main()
{
X* ptr = new X();
f( ptr );
// ptr is not valid anymore because the object were deleted
}
Jean
You could do it, but it could lead to undefined behavior, since there is no way to tell the second shared pointer that the reference count (the number of shared pointers pointing at the same object) increased. Then things like this could happen:
void f()
{
boost::shared_ptr<int> firstSmart(new int(23)); // firstSmart is the only
// manager of the int
int *raw = firstSmart.get();
boost::shared_ptr<int> secondSmart(raw); // secondSmart also manages
// the same int as firstSmart
// but secondSmart does not
// know about firstSmart
// and vice versa
}
when f exits secondSmart gets destroyed, destroying the shared int. Then firstSmart gets destroyed and attempts to destroy the already destroyed int thus leading to undefined behaviour.
Its not possible to do this.
Shared pointers work using reference counting. When you assign a resource (raw pointer) to a shared pointer a reference count object is created with count =1. When another shared pointer is created for the same resource the reference count object (which is shared between both the shared pointers) is updated to value count =2. If one shared pointer is deleted the count in the shared reference object is decremented and when it reached 0 the resource is destroyed.
For the above mechanism to work the first shared pointer should be created using something like shared_ptr px(p) and all subsequent ones using px (not 'p'). This way all the shared pointers created will know that they are holding same resource and share the same reference count object.
If you created another shared pointer using shared_ptr px(p) then you end up with two shared pointer not related to each other - i.e there reference count objects are not same. They both assume that they are holding distinct resource and each of them have distinct (different) reference count object with count =1. (You don’t want this).
Related
I have read a lot of issues created in regard to this but was not able to answer my question.
I have created a class as follows -
class exampleClass{
public:
exampleClass(int n){
cout<<"Created Class"<<endl;
this->number = n;
}
~exampleClass(){
cout<<endl<<"This class is destroyed Now"<<endl;
}
template<typename t> t
addNum(t a, t b){
return a + b;
}
void print(){
cout<<this->number<<endl;
}
private:
int number;
};
and I make 2 shared_ptr(or for that matter unique_ptr, error is same) as follows -
int main(){
exampleClass* object = new exampleClass(60);
std::shared_ptr<exampleClass> p1(object);
std::shared_ptr<exampleClass> p2 (object);
p1->print();
}
Now the error it throws at the end is -
free(): double free detected in tcache 2
Aborted (core dumped)
I am not able to understand why the error at the end. Shouldn't the above code be equal to p2 =p1(in case of shared_ptr) or p2 = std::move(p1) for unique_ptr as both the pointers are for the same object?
TIA
PS - The title might be a little misleading or not accurate,but I did not know what exactly should be a title.
When you create a smart pointer, it will take ownership of the pointer, and deletes it when it goes out of scope (or when the last reference is done in case of a shared pointer).
When you create two smart pointers from the same raw pointer they both will delete the pointer at the end of their life, because they don't know about each other.
int main()
{
// Create a shared pointer with a new object
std::shared_ptr<exampleClass> p1 = std::make_shared<exampleClass>(60);
// Now you can safely create a second pointer from your existing one.
std::shared_ptr<exampleClass> p2 = p1;
p1->print();
p2->print();
}
When you create a shared_ptr from a raw pointer, it takes ownership of the raw pointer, and when the smart pointer goes out of scope, it will call delete on the owned resource. Giving the same raw pointer to 2 different shared_ptrs causes a double free, as both of them will try to free the resource.
If you need 2 shared_ptrs that share the same resource, you can copy the first one:
int main(){
exampleClass* object = new exampleClass(60);
std::shared_ptr<exampleClass> p1(object);
std::shared_ptr<exampleClass> p2 = p1;
}
This way they share ownership of the resource (sharaed_ptrs have an internal reference counter that tracks how many of them own a resource. When you copy a shared_ptr, the reference counter is incremented. When one goes out of scope, the reference counter is decremented. Only if the counter reaches zero is the resource freed) thus it will only be freed once, when the last shared_ptr owning the resource goes out of scope.
It's usually preferable to avoid explicitly writing out new and use make_shared, which does the allocation, creates the object for you and returns a shared_ptr that owns it:
auto object = std::make_shared<exampleClass>(60);
Some additional advanced reading in the topic, not strictly related to the question:
performance differences when using make_shared vs manually calling new: here
memory implications of using make_shared with weak_ptrs and large objects: here (Thanks for bringig this up #Yakk - Adam Nevraumont, this was new for me :))
As per my understanding, weak pointer is used to cyclic dependency problem occurs if we use all shared_ptr objects and if there is a cyclic dependency. Weak pointers are used to break the cycles. weak pointers achieves this by using lock() which will create shared pointer.
class A { shared_ptr<B> b; ... };
class B { weak_ptr<A> a; ... };
shared_ptr<A> x(new A); // +1
x->b = new B; // +1
x->b->a = x; // No +1 here
But now assume that I created lock calling x->b->a.lock(), so ref count of x will become 2. and if x leaves the scope, still there will be memory leak right? Because I created a shared pointer using lock() and ref count became 2. Please let me know whether my understanding is correct or not.
There are two distinct reference counts involved for a shared_ptr shared object:
The number of references to the object, i.e. shared_ptr instances.
The number of references to the control block, i.e. shared_ptr and weak_ptr instances.
A weak_ptr contributes only to the latter count. When all shared_ptr instances have been destroyed, the object deleter is called, which usually is the default one that destroys the object. The control block still exists if there are weak pointers. When also all weak pointers have been destroyed, the control block is destroyed.
So (ignoring possible optimization of caching object pointer directly in each shared_ptr instance), in your case you have x pointing (hidden for you) to the control block, which has a pointer to the A instance. And you have the b member of that instance pointing to a second control block, which has a pointer to a B instance. Finally that instance has a pointer to the control block that x points to, which is circular yes, but not a circularity of ownership.
As in title. This question probably already has an answer but I failed to find one.
The fundamental conceptual difference between a naked pointer and a weak_ptr is that if the object pointed to is destroyed, the naked pointer won't tell you about it. This is called a dangling pointer: a pointer to an object that doesn't exist. They're generally hard to track down.
The weak_ptr will. In order to use a weak_ptr, you must first convert it into a shared_ptr. And if that shared_ptr doesn't point to anything, then the object was deleted.
For example:
#include <iostream>
#include <memory>
std::weak_ptr<int> wp;
void test()
{
auto spt = wp.lock(); // Has to be copied into a shared_ptr before usage
if (spt) {
std::cout << *spt << "\n";
} else {
std::cout << "wp is expired\n";
}
}
int main()
{
{
auto sp = std::make_shared<int>(42);
wp = sp;
test();
}
test();
}
Output
42
wp is expired
A raw pointer is (at least normally) simply an address. You can't tell anything about what it points at from the pointer itself.
A weak_ptr is always associated with a shared_ptr, so we probably need to start with a shared_ptr to make any sense of a weak_ptr.
A shared_ptr is reference counted, so it keeps track of how many references (pointers) to an object exist, and automatically destroys the object when no more references to that object exist.
As I already said, a weak_ptr is associated with a shared_ptr. Unlike a shared_ptr, the existence of a weak_ptr does not increment the reference count for the pointee object. To use a weak_ptr, you must first convert it to a shared_ptr. If the current reference count is positive, that will succeed, and converting the weak_ptr to a shared_ptr will increment the reference count to signify that the converted pointer is a "real" reference to the object. If, on the other hand, the reference count is already zero (meaning the pointee object has already been destroyed) the attempt to convert the weak_ptr to a shared_ptr will simply fail.
A shared_ptr means shared ownership of the pointee object. The pointee object will remain in existence as long as at least one shared_ptr to that object exists, but as soon as the last shared_ptr to the object is destroyed, so will the pointee object.
A weak_ptr means non-owning access to the pointee object. It allows access if the object exists. If the object has been destroyed, it tells you that the pointee object no longer exists rather than attempting to access the destroyed object.
Simple question here: are you allowed to explicitly delete a boost::shared_ptr yourself? Should you ever?
Clarifying, I don't mean delete the pointer held by the shared_ptr. I meant the actual shared_ptr itself. I know most people suggest to not do it, so I was just wondering if it's OK to explicitly do it.
Your question isn't clear. If you've allocated a shared_ptr dynamically then you're certainly allowed to delete it whenever you want.
But if you're asking whether you're allowed to delete whatever object is being managed by the shared_ptr, then the answer is ... it depends. If shared_ptr::unique returns true, then calling shared_ptr::reset will delete the managed object. However, if shared_ptr::unique returns false, it means there are more than one shared_ptrs sharing ownership of that object. In this case a call to reset will only result in the reference count being decremented by 1, actual deletion of the object will take place when the last shared_ptr managing that object either goes out of scope or is itself reset.
EDIT:
After your edit, it seems you are asking about deleting a dynamically allocated shared_ptr. Something like this:
auto sp = new boost::shared_ptr<int>( new int(42) );
// do something with sp
delete sp;
This is allowed and will work as expected, although it would be an unusual use case. The only caveat is that if in between the allocation and deletion of sp you create another shared_ptr that shares ownership of the object, deleting sp will not result in deletion of the object, that will only happen when the reference count for the object goes to 0.
[Edit: you can delete a shared_ptr if and only if it was created with new, same as any other type. I can't think why you'd create a shared_ptr with new, but there's nothing stopping you.]
Well, you could write delete ptr.get();.
Doing so leads almost inevitably to undefined behavior either when the other shared owners use their shared_ptr to access the now-deleted object, or the last shared_ptr to the object is destroyed, and the object gets deleted again.
So no, you shouldn't.
The purpose of shared_ptr is to manage an object that no one "person" has the right or responsibility to delete, because there could be others sharing ownership. So you shouldn't ever want to, either.
If you want to simulate the count decrement, you can do it manually on the heap like so:
int main(void) {
std::shared_ptr<std::string>* sp = new std::shared_ptr<std::string>(std::make_shared<std::string>(std::string("test")));
std::shared_ptr<std::string>* sp2 = new std::shared_ptr<std::string>(*sp);
delete sp;
std::cout << *(*sp2) << std::endl; // test
return 0;
}
Or on the stack using std::shared_ptr::reset() like so:
int main(void) {
std::shared_ptr<std::string> p = std::make_shared<std::string>(std::string("test"));
std::shared_ptr<std::string> p2 = p;
p.reset();
std::cout << *p2 << std::endl; // test
return 0;
}
But it's not that useful.
You cannot force its reference count to zero, no.
Think about what would be required for that to work. You would need to go to each place the shared_ptr is used and clear it.
If you did force the shared pointer to delete and set it to NULL, it would be just like a weak_ptr. However, all those places in the code using that shared_ptr are not ready for that and expect to be holding a valid pointer. They have no reason to check for NULL, and so those bits of code would crash.
Expliticly deleting comes in handy in some (very?) rare cases.
In addition to explicitly deleting, sometimes you HAVE to explicitly destruct a shared pointer when you are 'deleting' it!
Things can get weird when interfacing with C code, passing a shared_ptr as an opaque value.
For example I have the following for passing objects to and from the Lua scripting language which is written in C. (www.lua.org)
static void push( lua_State *L, std::shared_ptr<T> sp )
{
if( sp == nullptr ) {
lua_pushnil( L );
return;
}
// This is basically malloc from C++ point of view.
void *ud = lua_newuserdata( L, sizeof(std::shared_ptr<T>));
// Copy constructor, bumps ref count.
new(ud) std::shared_ptr<T>( sp );
luaL_setmetatable( L, B::class_name );
}
So thats a shared_ptr in some malloc'd memory. The reverse is this... (setup to be called just before Lua garbage collects an object and 'free's it).
static int destroy( lua_State *L )
{
// Grab opaque pointer
void* ud = luaL_checkudata( L, 1, B::class_name );
std::shared_ptr<T> *sp = static_cast<std::shared_ptr<T>*>(ud);
// Explicitly called, as this was 'placement new'd
// Decrements the ref count
sp->~shared_ptr();
return 0;
}
void ClassName::LocalMethod( )
{
boost::shared_ptr<ClassName> classNamePtr( this );
//some operation with classNamePtr
return;
}
Here the object is getting released when it returns from LocalMethod() since classNamePtr is out of scope. Isn't the shared_ptr smart enough to know that the ClassName object is still in scope and not to delete it?
What does it mean to create a shared_ptr to an object? It means that the holder of the shared_ptr now assumes ownership over the object. Ownership meaning that the object will be deleted when he so desires. When the holder of the shared_ptr destroys its shared_ptr, that will cause the object to potentially be destroyed, assuming that there are no other shared_ptrs to that object.
When a shared_ptr is a member of a class, that means that the lifetime of the object pointed to by the shared_ptr is at least as long as the object that the shared_ptr is a member of. When a shared_ptr is on the stack, this means that the lifetime of the object that the shared_ptr is pointing to will be at least as long as the scope it was created in. Once the object falls off the stack, it may be deleted.
The only time you should ever take a pointer and wrap it into a shared_ptr is when you are allocating the object initially. Why? Because an object does not know whether it is in a shared_ptr or not. It can't know. This means that the person who creates the original shared_ptr now has the responsibility to pass it around to other people who need to share ownership of that memory. The only way shared ownership works is through the copy constructor of shared_ptr. For example:
shared_ptr<int> p1 = new int(12);
shared_ptr<int> p2 = p1.get();
shared_ptr<int> p3 = p1;
The copy constructor of shared_ptr creates shared ownership between p1 and p3. Note that p2 does not share ownership with p1. They both think they have ownership over the same memory, but that's not the same as sharing it. Because they both think that they have unique ownership of it.
Therefore, when the three pointers are destroyed, the following will happen. First, p3 will be destroyed. But since p3 and p1 share ownership of the integer, the integer will not be destroyed yet. Next, p2 will be destroyed. Since it thinks that it is the only holder of the integer, it will then destroy it.
At this point, p1 is pointing to deleted memory. When p1 is destroyed, it thinks that it is the only holder of the integer, so it will then destroy it. This is bad, since it was already destroyed.
Your problem is this. You are inside an instance of a class. And you need to call some functions of yours that take a shared_ptr. But all you have is this, which is a regular pointer. What do you do?
You're going to get some examples that suggest enable_shared_from_this. But consider a more relevant question: "why do those functions take a shared_ptr as an argument?"
The type of pointer a function takes is indicative of what that function does with its argument. If a function takes a shared_ptr, that means that it needs to own the pointer. It needs to take shared ownership of the memory. So, look at your code and ask whether those functions truly need to take ownership of the memory. Are they storing the shared_ptr somewhere long-term (ie: in an object), or are they just using them for the duration of the function call?
If it's the latter, then the functions should take a naked pointer, not a shared_ptr. That way, they cannot claim ownership. Your interface is then self-documenting: the pointer type explains ownership.
However, it is possible that you could be calling functions that truly do need to take shared ownership. Then you need to use enable_shared_from_this. First, your class needs to be derived from enable_shared_from_this. Then, in the function:
void ClassName::LocalMethod()
{
boost::shared_ptr<ClassName> classNamePtr(shared_from_this());
//some operation with classNamePtr
return;
}
Note that there is a cost here. enable_shared_from_this puts a boost::weak_ptr in the class. But there is no virtual overhead or somesuch; it doesn't make the class virtual. enable_shared_from_this is a template, so you have to declare it like this:
class ClassName : public boost::enable_shared_from_this<ClassName>
Isn't the shared_ptr smart enough to know that the ClassName object is
still in scope and not to delete it?
That's not how shared_ptr works. When you pass a pointer while constructing a shared_ptr, the shared_ptr will assume ownership of the pointee (in this case, *this). In other words, the shared_ptr assumes total control over the lifetime of the pointee by virtue of the fact that the shared_ptr now owns it. Because of this, the last shared_ptr owning the pointee will delete it.
If there will be no copies of classNamePtr outside of ClassName::LocalMethod(), you can pass a deleter that does nothing while constructing classNamePtr. Here's an example of a custom deleter being used to prevent a shared_ptr from deleting its pointee. Adapting the example to your situation:
struct null_deleter // Does nothing
{
void operator()(void const*) const {}
};
void ClassName::LocalMethod()
{
// Construct a shared_ptr to this, but make it so that it doesn't
// delete the pointee.
boost::shared_ptr<ClassName> classNamePtr(this, null_deleter());
// Some operation with classNamePtr
// The only shared_ptr here will go away as the stack unwinds,
// but because of the null deleter it won't delete this.
return;
}
You can also use enable_shared_from_this to obtain a shared_ptr from this. Note that the member function shared_from_this() only works if you have an existing shared_ptr already pointing to this.
class ClassName : public enable_shared_from_this<ClassName>
{
public:
void LocalMethod()
{
boost::shared_ptr<ClassName> classNamePtr = shared_from_this();
}
}
// ...
// This must have been declared somewhere...
shared_ptr<ClassName> p(new ClassName);
// before you call this:
p->LocalMethod();
This is the more appropriate, "official" method and it's much less hackish than the null deleter method.
It could also be that you don't actually need to create a shared_ptr in the first place. What goes into the section commented //some operation with classNamePtr? There might be an even better way than the first two ways.