Typedef a shared_ptr type with a static custom deleter, similar to unique_ptr

后端 未结 2 970
萌比男神i
萌比男神i 2020-12-19 19:26

I have read through many questions on SO on custom deleter for shared_ptr and unique_ptr, and the difference between the two. But, I still haven\'t

2条回答
  •  不知归路
    2020-12-19 20:09

    The other answer provided here was that something close to what I asked could be done through function returns of unique_ptr with custom deleter, which can be implicitly converted to a shared_ptr.

    The answer given was that a deleter defined as a type trait was not possible for std::shared_ptr. The answer suggested as an alternative, to use a function which returns a unique_ptr, implicitly converted to a shared_ptr.

    Since this isn't part of the type, it is possible to make a simple mistake, leading to memory leaks. Which is what I wanted to avoid.

    For example:

    // Correct usage:
    shared_ptr s(createSurface(IMG_Load("image.png")));
    
    // Memory Leak:
    shared_ptr s(IMG_Load("image.png"));
    

    The concept I want to express is having the deleter as part of the type (which unique_ptr allows), but with the functionality of a shared_ptr. My suggested solution is deriving from shared_ptr, and providing the deleter type as a template argument. This takes up no additional memory, and works in the same way as for unique_ptr.

    template>
    struct shared_ptr_with_deleter : public std::shared_ptr
    {
      explicit shared_ptr_with_deleter(T* t = nullptr)
          : std::shared_ptr(t, D()) {}
    
      // Reset function, as it also needs to properly set the deleter.
      void reset(T* t = nullptr) { std::shared_ptr::reset(t, D());  }
    };
    

    Together with a deleter class (Thanks Jonathan Wakely. Way cleaner than my macro (now removed)):

    struct SDL_Deleter
    {
      void operator()(SDL_Surface* p) const { if (p) SDL_FreeSurface(p); }
      void operator()(SDL_RWops* p) const { if (p) SDL_RWclose(p); }
    };
    
    using SurfacePtr = std::unique_ptr;
    using SurfaceShPtr = shared_ptr_with_deleter;
    
    using RWopsPtr = std::unique_ptr;
    using RWopsShPtr = shared_ptr_with_deleter;
    

    Instances with SurfaceShPtr members are type guaranteed to clean up properly, the same as for SurfacePtr, which is what I wanted.

    // Correct Usage (much harder to use incorrectly now):
    SurfaceShPtr s(IMG_Load("image.png"));
    
    // Still correct usage
    s.reset(IMG_Load("other.png"));
    

    I'll leave this up for a while, for comments, etc, without accepting the answer. Maybe there are even more dangerous caveats I've missed (having a non-virtual destructor not being one, as the parent shared_ptr is given charge of the deletion).

提交回复
热议问题