What's the difference between raw pointer and weak_ptr? - c++

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.

Related

C++ rvalue shared_ptr and rvalue weak_ptr

std::shared_ptr<std::string> test() {
return std::make_shared<std::string>("sdsd");
}
cout << *test() << endl;
The above code works. Can someone please let me know where is the "sdsd" string stored. I was expecting it to error out as rvalue is a temporary object. Where is the rvalue copied to and stored in?
With weak_ptr
std::weak_ptr<std::string> test() {
return std::make_shared<std::string>("sdsd");
}
cout << *test().lock() << endl;
Interestingly the above code errors out. What's the difference?
In
std::shared_ptr<std::string> test() {
return std::make_shared<std::string>("sdsd");
}
the constructed shared_ptr is returned and lives on as a temporary variable long enough to be used by the caller before going out of scope and freeing the string allocated by make_shared.
The "sdsd" string is stored in dynamic storage owned by the returned shared_ptr.
In
std::weak_ptr<std::string> test() {
return std::make_shared<std::string>("sdsd");
}
the shared_ptr wasn't returned and went out of scope, taking the string allocated by make_shared with it. Since this was the only existing copy of the shared_ptr this leaves the returned temporary weak_ptr variable connected to an expired share_ptr. If you test the return value of lock you'll see it's a default-constructed shared_ptr holding a null pointer.
Documentation for std::weak_ptr::lock.
The "sdsd" string isn't stored anywhere after test returns. It departed when the shared_ptr that owned it went out of scope.
The above code works. Can someone please let me know where is the "sdsd" string stored. I was expecting it to error out as rvalue is a temporary object. Where is the rvalue copied to and stored in?
The memory was allocated in the call to make_shared.
Interestingly the above code errors out. What's the difference?
The difference is that the lock operation on a weak_ptr can fail if there does not exist at least one shared_ptr to the object. That's the purpose of weak_ptr -- to allow the object to be freed and access it only if it still exists.

Is unique_ptr constructor initializes the raw pointer and also unique_ptr destructor deletes the associated raw pointer?

First time I am using smart pointers in my project. While using unique_ptr, I got some doubts regarding unique_ptr and raw pointer combination. And the way unique_ptr works internally.
Could some one please explain/answer based on my understanding as mentioned below, so that I can go ahead and use the smart pointers.
Below is the example:
class A
{
public:
void show()
{
cout<<"A::show()"<<endl;
}
};
int main()
{
unique_ptr<A> p1 (new A);
p1 -> show();
// returns the memory address of p1
cout << p1.get();
retrun 0;
}
From the above example,
When creating unique_ptr object "p1" we are providing raw pointer. Internally, unique_ptr constructor will initialize the unique_ptr with the raw pointer. Is my understanding correct?
As per the unique_ptr definition, "The pointer is exclusively owned by one object or a resource".
Based on the above statement, in our scenario, "raw pointer" is exclusively
owned by the unique_ptr object "p1". Am I correct?
And also after the statement, cout << p1.get(); (In the above sample program) as it is going out of scope, internally, the destructor of the unique_ptr called and it deletes the associated raw pointer. Is my understanding correct?
Finally, once deletes the associated raw pointer is the unique_ptr object will become empty?
When creating unique_ptr object "p1" we are providing raw pointer. Internally, unique_ptr constructor will initialize the unique_ptr with the raw pointer. Is my understanding correct?
Yes. The unique pointer will hold the same address.
As per the unique_ptr definition, "The pointer is exclusively owned by one object or a resource".
Based on the above statement, in our scenario, "raw pointer" is exclusively owned by the unique_ptr object "p1". Am I correct?
Yes. The only reference, the one that owns the resource and will free it, is the unique pointer. Note however that it's not the pointer that's owned, but the object it points at. The unique_ptr didn't take ownership of the raw pointer, it took ownership of the object (the resource) that is at the address the raw pointer provided.
And also after the statement, cout << p1.get(); (In the above sample program) as it is going out of scope, internally, the destructor of the unique_ptr called and it deletes the associated raw pointer. Is my understanding correct?
Yes. The unique ptr will cause the deletion of its internal raw pointer when it goes out of scope.
Finally, once deletes the associated raw pointer is the unique_ptr object will become empty?
Doesn't have to. Since the deletion happens when the unique_ptr object itself is being destroyed, there is no real need to "empty" it. It's about to go out of existence anyway, so its value is immaterial.

Convert a shared_ptr to regular a pointer

We have a function that returns a new allocated object as a output argument (ref to pointer).
MyFunc(MyObject*& obj)
{
obj = new MyObject();
}
Which is called like so:
Object* obj;
MyFunc(obj);
Internally the function does quite a bit and uses shared_ptr for memory management. When it is done, the object we would like to return is referenced by a shared_ptr. I am struggling on how to return our new allocated object as a regular pointer.
We would like to continue to use shared_ptr internally to reduce risks, but it does not seem to make sense to return a shared_ptr as the caller takes complete ownership over the returned value (the called function or object no longer needs or keeps a reference to the returned data) and we want them to have flexibility.
Does anyone have any suggestions for allowing us to use shared_ptr internally but have a regular pointer interface? Thanks
If for some reason you cannot/want not use std::unique_ptr or std::auto_ptr (for example if you need to have multiple owners internally during creation for some reason or your underlying methods require std::shared_ptr to be passed around), you can still make it work with std::shared_ptr by using custom deleter, as described here: https://stackoverflow.com/a/5995770/1274747
In the principle, after you're done before the return, you switch the deleter to not actually delete the instance (make the deleter "null") and then return by shared_ptr get(). Even after all shared_ptr objects are destroyed, the memory will not be deleted (as the nulled deleter will skip the deletion).
There is also a link in the comments not so well visible, which might be of your interest:
http://paste.ubuntu.com/23866812/
(not sure though if it would really work without the shared ownership of the switch in all cases, would need to test)
EDIT
As expected, with the linked simple disarmable deleter from the pastebin you need to be careful, because the deleter is actually copied for storing in std::shared_ptr.
But you can still make it work by using std::ref:
MyFunc(MyObject*& obj)
{
DisarmableDelete<MyObject> deleter;
std::shared_ptr<MyObject> ptr(new MyObject(), std::ref(deleter));
// do what is necessary to setup the object - protected by deleter
// ...
// disarm before return
deleter._armed = false;
obj = ptr.get();
// deleter disarmed - object not freed
}
And just for completeness (and to avoid a potential future broken link), here is the implementation of DisarmableDelete from http://paste.ubuntu.com/23866812/.
template <typename T, typename Deleter = typename std::default_delete<T> >
struct DisarmableDelete : private Deleter {
void operator()(T* ptr) { if(_armed) Deleter::operator()(ptr); }
bool _armed = true;
};
It depends on who "owns" the pointer, once it has been exposed to the 'outside world.' Ownership essentially boils down to: "who is responsible for freeing this memory, later?"
It can be answered with a simple question: when MyFunc is called, is the caller responsible for deleting the pointer when it's done?
If so, then MyFunc needs to 'release' the ownership, otherwise the shared_ptr will automatically delete the pointer, when it goes out of scope. This actually can't be done, using shared_ptr. You need to use a unique_ptr instead, and call unique_ptr::release().
If not - if MyFunc will simply use the resulting pointer and forget about it without delete-ing it - then you can simply return the 'raw' pointer using shared_ptr::get(). You must be careful, because this implies that the shared_ptr still exists elsewhere in your code.
I can see four alternatives, as highlighted below. They are all horrible, and short of switching your ownership to std::unique_ptr<T> and returning via obj = ptr.release(); I can only offer a hack where the argument is assigned to the pointer upon destruction, but you still need to catch the exception and test whether the pointer was assigned.
#include <iostream>
#include <memory>
#include <exception>
struct foo {
void bar() const { std::cout << this << " foo::bar()\n"; }
~foo() { std::cout << this << " deleted\n"; }
};
void f1(foo*& obj) {
obj = new foo;
// do stuff... if an exception is thrown before we return we are
// left with a memory leak
}
void f2(foo*& obj) {
auto holder = std::make_shared<foo>();
// do stuff.. if an exception is thrown the pointer will be
// correclty deleted.
obj = holder.get(); // awesome, I have a raw pointer!
} // oops, the destructor gets called because holder went out of
// scope... my pointer points to a deleted object.
void f3(foo*& obj) {
auto holder = std::make_unique<foo>();
// do stuff.. if an exception is thrown the pointer will be
// correclty deleted.
obj = holder.release(); // awesome, I have a raw pointer!
} // no problem whem holder goes out of scope because it does not own the pointer
void f4(foo*& obj) {
// a super-weird hack that assigns obj upon deletion
std::shared_ptr<foo> holder(new foo, [&obj](foo*& p){ obj = p; });
throw std::exception();
} // no problem whem holder goes out of scope because it does not own
// the pointer... but if an execption is throw we need to delete obj
int main() {
foo* p1;
f1(p1);
p1->bar();
foo* p2;
f2(p2);
// p2->bar(); // error
foo* p3;
f3(p3);
p3->bar();
foo* p4;
try {
f4(p4);
} catch(...) {
std::cout << "caught an exception... test whether p4 was assigned it\n";
}
p4->bar(); // I still need to delete this thing
}
The point of shared_ptr is to express shared ownership.
Anything that does not share in the ownership of an object -- that doesn't have the right to make an object lifetime last longer, and the object lifetime is the union of the shared owners request for object lifetime -- should not use a shared_ptr.
Here, you have a shared_ptr and you are going to return it. At that point, you are violating the assumptions of shared_ptr; anyone who kept a shared_ptr copy expects that its content can last as long as it requests.
Meanwhile, the calling code thinks it owns the MyObject* raw pointer you passed it.
This is an example of misuse of shared_ptr.
Saying "we have memory management issues, use shared_ptr" doesn't fix memory management issues. Proper use of shared_ptr requires care and design, and the design must be that when the end of the lifetime of the object in question is shared by 2 or more pieces of data and/or code.
The internal code, if it does not own the pointer, should use either something like an observer_ptr<T> or a raw T* (the first to make it clear it doesn't own the object).
Ownership should be explicit, and in a unique_ptr. It can then call .release() to pass ownership to a raw pointer if required; in practice, I would change your signature to take a unique_ptr&, or have it return a unique_ptr.
The caller would then call .release() when they want to use some other object lifetime management system, or that object lifetime management system should consume unique_ptrs (thus being extremely clear about taking ownership of things).
Use a non-hack solution.
Such as a std::shared_ptr<std::unique_ptr<T>>. In this case, you have shared ownership of a unique ownership.
The unique_ptr can have its ownership taken from it (via .release()). When it does so, all of the shared_ptrs that still exist will have their unique_ptr also be cleared.
This places the shared unique ownership front and center, instead of hacking a non-deleter into a shared_ptr and having dangling shared_ptrs that think they have ownership over data but do not.
The best approach is to use internally unique_ptr and call its .release() method before returning the raw pointer.
If you are stuck to use shared_ptr internally, an option is to create it specifying a custom, "noop" deleter, that just does nothing when shared_ptr is destroyed (instead of calling delete on the owned pointer). The get the raw pointer from shared_ptr as usual (.get() method).
An example of such a deleter can be found in the Boost library (null_deleter).
But please note that doing this effectively "disable" the usefulness of having a shared_ptr at all...
If your managing the lifespan of the allocated object internally with std::shared_ptr, and are returning a raw pointer for access and don't want this pointer to affect the ref count, you can return the raw pointer by calling shared_ptr.get().
It can be problematic to return smart pointers if your using a tool like Swig to generate wrappers for other languages.

boost::shared_ptr from pointer

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).

shared_ptr deletes the object

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.