Ponkadoodle
Ponkadoodle

Reputation: 5957

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?

Upvotes: 7

Views: 2647

Answers (4)

Anton Sukhinov
Anton Sukhinov

Reputation: 167

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

Upvotes: 6

badger the bold
badger the bold

Reputation: 421

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.

Upvotes: 1

ks1322
ks1322

Reputation: 35716

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;
}

Upvotes: 4

Andrew
Andrew

Reputation: 24846

Store shared_ptr in you Cache as Vaughn Cato suggests. In order to remove a texture from the cache when no one uses it just check if use_count function of the shared_ptr returns 1, which means cache is the only owner

Upvotes: 0

Related Questions