Search code examples
c++memory-managementshared-ptr

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?


Solution

  • 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