Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass an object to a function expecting shared_ptr without actually sharing ownership

First of all, I do realize this completely contradicts the purpose of a shared_ptr. I am dealing with some library code where instances of a ParticleSystem expect to have a shared_ptr passed to them during construction to set the texture used for each particle. The thing is, I've already built the rest of my program in a way where my textures have concrete ownership (if that's the right term) - the TextureCache owns all Textures. So I need a way to work with this ParticleSystem class without allowing it to delete my textures. If I were to simply create a new instance like ParticleSystem(std::shared_ptr<Texture>&myTexture) then it would attempt to destroy the texture upon its destruction (which is an unwanted and invalid operation, since my textures aren't even created with new).

The cleanest way I see around this problem is something like this:

  1. Create a shared_ptr holding the texture in the function that creates the ParticleSystem.
  2. Then using placement new, reconstruct the shared_ptr in the same memory location as the shared_ptr I just created. The texture will now have a reference count of 2.
  3. Create the particle system.
  4. Let the shared_ptr go out of scope. Its deconstructor will be called since it was allocated on the stack, and it will decrement the reference count only by 1. Thus the reference count for the object will always be 1 greater than it truly is, and so it will never be automatically destroyed.

I believe this solution is sound, but it still feels incredibly hackish. Is there a better way to solve my problem?

like image 333
Ponkadoodle Avatar asked Aug 26 '12 05:08

Ponkadoodle


3 Answers

If you want to pass unmanaged pointer (that you manage by yourself) to code expecting smart pointer such as shared_ptr, you can just disable «smart» pointer functionality by creating empty, but not-null shared_ptr via aliasing constructor:

Texture* unmanagedPointer = ...
shared_ptr<Texture> smartPointer(shared_ptr<Texture>(), unmanagedPointer);

This solution is more efficient and shorter than custom deleter others suggested, since no control block allocation and reference counting is going on.

Some additional details can be found here:

What is the difference between an empty and a null std::shared_ptr in C++?

How to avoid big memory allocation with std::make_shared

like image 99
Anton Sukhinov Avatar answered Oct 06 '22 21:10

Anton Sukhinov


You can create shared_ptr with custom deleter that does nothing. This will prevent deleting textures owned by this shared_ptr.

struct null_deleter
{
    void operator()(void const *) const
    {
    }
};

shared_ptr<Texture> CreateTexture(Texture* myTexture)
{
    shared_ptr<Texture> pTexture(myTexture, null_deleter());
    return pTexture;
}
like image 30
ks1322 Avatar answered Oct 06 '22 21:10

ks1322


shared_ptr allows you to supply a custom deleter. So shared_ptr can be used for memory allocaed with malloc or whatever memory allocation scheme you're using, you could even use it to automtically unlock a mutex or close a file, but I digress. You could create a shared_ptr with a null deleter which would not do anything when its referene count reaches 0.

like image 1
badger the bold Avatar answered Oct 06 '22 19:10

badger the bold