How to manage resources that cannot be deep-copied in C++ - c++

I am creating a class that manages a resource that should not be "deep copied", that is, there can only ever be one instance of the underlying resource, even if multiple objects have access this same resource.
However, allowing multiple objects to access this resource is also dangerous, as one object could go out of scope, and self-destruct, which will also destroy the resource. In this case, is it reasonable to only define a move constructor (without allowing for shallow copies)? Or is there some way to support shallow copies, so that multiple objects can reference the same resource, but the resource will not be destroyed if at least one object still has access to the resource?
For context, the resource being managed is an OpenGL shader, and each object has the ID of this shader as one of it's members, which it uses to tell OpenGL to delete the shader when necessary.

I think that you are looking for std::shared_ptr or parallel solution; std::shared_ptr is used to share pointers to a single object, which is destroyed only after all of the shared pointers are cleared. Thus, while we still have a reference to the object, it remains valid.
Even if you are not looking to use shared_ptr, the idea behind it is to use a reference counter, which is shared by all objects that refer to the same resource - each time you call a constructor/copy constructor/copy-assignment, you increase the shared counter by 1, and in the destructor, you decrease it by 1, and if (and only if) it reaches 0 then you release the underlying resource.
Also, for completeness of the answer, I should add that for std::shared_ptr, there is in addition std::weak_ptr, which denotes access without shared ownership - it allows one to access a resource held by a std::shared_ptr while it is alive, but can be reset automatically if all of the std::shared_ptr that refer to the resource are destroyed. It is rare to see usage of it, but it is possible to use it, nevertheless.

Related

memory management similar to std::shared_ptr without atomic features?

Lets say that I have an object which is expensive to copy, and I cannot move it. I am passing this object to other objects, which it will be a member of. I am not able to take a reference/pointer to the original object due to not knowing the lifetime of it.
If I am to instantiate the original expensive object in a std::shared_ptr, I can avoid deep copying the expensive object and am able to pass the object around with it being more efficient than copying the expensive object. Furthermore, it is nicely cleaned up/destructed when the final instance of it goes out of scope. The issue is, copying a std::shared_ptr is expensive, and I would prefer to avoid this. I can create a wrapper for this behavior which initializes a single instance of the expensive object, reference tracks it, and has proper destruction behavior at the last instance of it going out of scope - but I am not sure if there is a easier/more preferred alternative.
Any help here?
Thanks.

Syntax for converting expired weak_ptr<T> to shared_ptr<T>

From what I've read, a shared_ptr<T> does not get de-allocated until both strong references AND weak references to it are dropped.
I understand a shared object to be considered expired when there are no more strong references to it. The standard lock() function of a weak_ptr<T> therefore fails in such a case because the object is considered 'expired'.
However, if the deleter of a shared pointer is overridden such that the managed object is not deleted, then it should be valid to generated shared_ptr<T> from a weak_ptr<T> - but I cannot find the right syntax to do this.
std::shared_ptr<int> s_ptr(new(42), D());
std::weak_ptr<int) w_ptr(s_ptr);
s_ptr.reset();
s_ptr = std::shared_ptr<int>(w_ptr, false);
EDIT
To clarify this a bit further, I'm trying to construct an object pool of re-usable shared_ptr<T>. The reason behind this is because every use of shared_ptr results in one or more heap memory allocations. So I've added a deleter to every shared_ptr<T> which stores a weak_ptr<T> reference such that then the deleter gets called it should be able to re-add itself to a pool of available shared_ptr<T> objects (with managed object intact). By keeping a weak_ptr<T> stored inside the shared_ptr<T>'s deleter, it therefore shouldn't stop the deleter from being called.
The end goal is to obtain a smart pointer that doesn't do consisant heap allocation - or at least only a small number.
From what I've read, a shared_ptr does not get de-allocated until both strong references AND weak references to it are dropped.
Wrong. a std::shared_ptr has two blocks - a control block that contains reference counts, etc, then another block for the actual data.
When the shared count goes to 0, the data block is (usually) deallocated. This is why it is illegal to make a std::shared_ptr from a expired std::weak_ptr.
On another note, why would you EVER want this functionality? It ruins the entire point of std::weak_ptr, which is to be able "store" a pointer to a object stored by std::shared_ptr without incrementing its reference count.
If you want to do that, then please just use a std::shared_ptr
If pooling control blocks is a good idea, the library implementation may already be doing it. In fact, the implementation of new itself may already be doing pooling to support similar memory usage patterns.
Furthermore, you can achieve what I think your goals are by using make_shared rather than invoking new and passing it into a shared_ptr constructor; one of the reasons for the existence of this helper function is that it can be written to use a single allocation to allocate both the control block and the new object you're creating at the same time.

Can I make a shared object pool using std::shared_ptr and weak_ptr without a custom destructor?

I want to have a pool of shared objects whose class name is Shader. I want a "client" to be able to request a Shader with certain parameters from the pool manager, and if there's one already in the pool the manager will return a pointer or reference to it, otherwise it creates a new Shader and adds it to the pool before returning its reference. Easy enough so far.
However, I also want the Shaders to be automatically deleted when all the clients have finished with them. Is this possible by implementing the pool as a container of std::weak_ptr and returning std::shared_ptr to the clients? Ie if I call weak_ptr::lock() multiple times on the same object are the shared_ptrs it returns linked to each other correctly, or does it return independent shared_ptrs leading to undefined behaviour? cpprefrence.com implies the latter, but they don't explictly point out the danger of that, and it seems to me that an opportunity was missed to make weak_ptr considerably more useful.
Calling weak_ptr::lock() multiple times is ok, the returned shared_ptr is configured properly, i.e. all shared_ptr-s having the common pointee are sharing the reference counter.
Note that lock() returns non-null pointer iff there is at least one shared_ptr pointing to the object. If there was none then the object would have been already destroyed. Hence lock() absolutely must implement sharing right otherwise it would break on every call.
Finally shared_ptr supports custom deleter functions, they may be useful to remove stale entries from the cache.
Is this possible by implementing the pool as a container of std::weak_ptr and returning std::shared_ptr to the clients?
Yes.
Ie if I call weak_ptr::lock() multiple times on the same object are
the shared_ptrs it returns linked to each other correctly, or does it
return independent shared_ptrs leading to undefined behaviour?
weak_ptr::lock() returns shared_ptr linked to other shared_ptrs to that object or empty shared_ptr when there are no other shared_ptrs (IOW weak_ptr::expired() is true).
cpprefrence.com implies the latter, but they don't explictly point out
the danger of that, and it seems to me that an opportunity was missed
to make weak_ptr considerably more useful.
I can not understand how you read that implication.

Create a non-owning shared_ptr?

I am pretty new to C++11 and am now working on improving my C++ skills by trying to avoid direct usage of pointers. I am trying to write a sprite manager that keeps track of previously loaded sprites and frees unused ones. I am trying to use shared_ptr (pointer to the bitmap) for this, but the manager also has to keep a shared_ptr to create the sprites with - so the reference count doesn't drop to 0. Can I somehow declare the "parent" shared_ptr in my manager non-owning so it doesn't count as a reference (and still create owning copies of that shared_ptr)?
Use a weak_ptr. That will solve your problem. You won't need to free them as they will be automatically freed. Use a lock on the weak_ptr to get an actual shared_ptr.
The use_count will also give you the current number of references.
shared_ptr are made to be owning. If you want a non-owning pointer at some part of your prgram use weak_ptr like so:
std::shared_ptr<Object> sp(new Object);
std::weak_ptr<Object>(sp);
You're trying to do a kind of "backseat driver" style of memory management; you want to use shared-ptr, but you also want to control when shared_ptr frees resources!
There are a couple of obvious things you could do here.
Just use shared_ptr and don't bother with any sort of memory management or resource ownership in your sprite manager class. Trust shared_ptr to do its job. If you need to know when a resource is destroyed, you can always use the observer pattern or the like, and have your resource class message the manager when it is destroyed. Of course, this means you can't ask your sprite manager to provide additional references to an existing sprite, which isn't so great.
Write your own smart pointer. It isn't necessarily trivial, but writing a resource-specific reference-counting smart pointer isn't rocket science (its a hell of a lot simpler than writing something like shared_ptr, for example). The manager can then terminate resources when there's only a single reference to them remaining (eg. its own reference).
Everyone else has already mentioned weak_ptr. Has all of the benefits of (1), only you can create additional shared_ptr instances referencing the same underlying resource.
You might also want to consider resource usage patterns, and the cost of loading resources. You may not necessarily want to destroy a resource as soon as your application stops referencing it; if it is requested again a second later, it might take some time to reload it. Lazily freeing resources when they've gone unused for a little while might be a better approach. Just a thought.

shared_ptr: what's it used for

I make a lot of use of boost::scoped_ptr in my code and it is great but I'm currently working with software that uses shared_ptr all over the place and I'm wondering if I'm missing something.
AFAIK a shared_ptr is only useful if different threads are going to be accessing the same data and you don't know what order the threads are going to finish (with the shared_ptr ensuring that the object exists until the last thread has finished with it).
Are there other use cases?
Threads are irrelevant here. What's relevant is whether it's easy to specify a point at which the object is no longer of use.
Suppose several different objects want to use the same object. It might be a pack of data, or for input/output, or some geometric object, or whatever. You want the shared object to be deleted after all of the using objects are deleted, and not a clock cycle before. Rather than figure out which owning object is going to have the longest lifespan (and that can change if you change the program, or perhaps through user interaction), you can use a shared_ptr to force this behavior.
It doesn't matter whether the using objects are in the same or different threads. Objects can have unpredictable lifetimes even if they're all in the same thread.
AFAIK a shared_ptr is only useful if
different threads are going to be
accessing the same data
Well, it's for situations where multiple owners own the same object pointed to by the smart pointer. They may access the smart pointers from different threads, and shared_ptr is usable in that area too, but that's not the main point. If the last owner loses its reference to the object pointed to, the shared_ptr mechanism deletes the object.
You can use a scoped_ptr if all you want to have is a pointer that is deleted when the scope it's created in is left (either by exceptions, by a goto to a place outside, or by normal control flow or some other mechanism). If you use it like that, there is no need to change to shared_ptr.
The difference between scoped_ptr and shared_ptr (and auto_ptr) is mainly copy semantics.
scoped_ptr is for "Resource Allocation Is Initialization" and is not copyable (it cannot be shared with other instances and ownership cannot be transferred)
shared_ptr is for automatic reclamation of memory when shared between multiple parties
auto_ptr is copyable (and transfers ownership when assigned)
Another important difference between shared_ptr and scoped_ptr is that only shared_ptr work with weak_ptr. Weak pointers are used to break cycles of shared pointers, thereby avoiding memory leaks, but weak_ptr can be used for more than that.
Shared and weak pointers may be used to express the difference between owning and non-owning references. Unambiguous ownership of data leads to a cleaner design, so when possible data objects should be owned by one other object through a shared_ptr. All other long-lived references to data objects should be weak pointers, expressing their non-ownership of the data. Each time any non-owning modules access the data, they need to convert the weak_ptr into a shared_ptr, at which point they may find that the data object no longer exists. However, while the non-owning modules access the data object, they hold it through transient shared_ptr, ensuring safe operation even if the owning object were to release the data.
As answered already, shared_ptr is about shared ownership. However, I would argue that shared ownership is generally a bad thing (exceptions exists, such as flyweight pattern) and it is better to identify an owner and put a scoped_ptr there.
A shared_ptr is a smart pointer type that does reference counting. If there's only one owner for the object (frequent case), then scoped_ptr is the right solution. If the object can be shared among multiple parts of the code, then shared_ptr won't let the object be destructed until all references to it have been released.