7

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?

AstroCB
  • 12,337
  • 20
  • 57
  • 73
Ponkadoodle
  • 5,777
  • 5
  • 38
  • 62
  • Why not have shared_ptrs in your texture cache? – Vaughn Cato Aug 26 '12 at 05:10
  • @Vaughn that is feasible, but it seems like an unnecessary extra level of indirection by tracking pointers to the textures rather than the textures themselves and it would be a misuse of shared pointers semantically since the texture cache is still responsible for deciding when it's OK to release the texture - it would not be *sharing* ownership. – Ponkadoodle Aug 26 '12 at 05:21

4 Answers4

5

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

Community
  • 1
  • 1
Anton Sukhinov
  • 158
  • 1
  • 8
3

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;
}
ks1322
  • 33,961
  • 14
  • 109
  • 164
1

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.

badger the bold
  • 421
  • 2
  • 4
0

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

Andrew
  • 24,218
  • 13
  • 61
  • 90