Typedef带有静态自定义删除器的shared_ptr类型,类似于unique_ptr

swa*_*log 3 c++ smart-pointers raii shared-ptr unique-ptr

我已经通过对定制删除这么多的问题读取shared_ptrunique_ptr,以及两者之间的差异.但是,我仍然没有找到这个问题的任何明确答案:

如何最好地创建一个充当shared_ptr自定义删除器的类型,类似于删除器如何unique_ptr作为类型定义的一部分?

为了unique_ptr使用,我使用了一个删除类,它处理各个类型的删除(为简洁起见,它只限于两种类型):

struct SDL_Deleter {                                
  void operator()( SDL_Surface* ptr ) { if (ptr) SDL_FreeSurface( ptr );} 
  void operator()( SDL_RWops* ptr )   { if (ptr) SDL_RWclose( ptr );} 
};

using SurfacePtr = std::unique_ptr<SDL_Surface, SDL_Deleter>;
using RWopsPtr = std::unique_ptr<SDL_RWops, SDL_Deleter>;
Run Code Online (Sandbox Code Playgroud)

哪个可以用于类似的东西

SurfacePtr surface(IMG_Load("image.png"));
Run Code Online (Sandbox Code Playgroud)

并呼吁SDL_FreeSurface毁灭.


这一切都很好.但是,如何实现同样的目标shared_ptr呢?其类型定义为

template< class T > class shared_ptr;
Run Code Online (Sandbox Code Playgroud)

并且提供自定义删除器的方法是通过构造函数.shared_ptr包装器的用户需要知道包装了哪个指针类型以及应该如何删除该指针,这感觉不对.实现与上述unique_ptr示例相同类型的使用的最佳方式是什么.

换句话说,我最终可能会:

SurfaceShPtr surface(IMG_Load("image.png"));
Run Code Online (Sandbox Code Playgroud)

而不是像

SurfaceShPtr surface(IMG_Load("image.png"),
                     [=](SDL_Surface* ptr){SDL_FreeSurface(ptr);});
Run Code Online (Sandbox Code Playgroud)

或者,稍微好一些

SurfaceShPtr surface(IMG_Load("image.png"),
                     SDL_Deleter());
Run Code Online (Sandbox Code Playgroud)

有没有办法做到这一点,而不必创建RAII包装类(而不是typedef),增加更多的开销?


如果答案是"这是不可能的".为什么不?

swa*_*log 5

这里提供的另一个答案是,通过unique_ptr使用自定义删除器的函数返回,可以通过隐式转换为a 来实现与我所要求的一致shared_ptr.

给出的答案是,定义为类型特征的删除器是不可能的std::shared_ptr.答案建议作为替代,使用一个返回a的函数unique_ptr,隐式转换为a shared_ptr.

由于这不是该类型的一部分,因此可能会犯一个简单的错误,导致内存泄漏.这是我想要避免的.

例如:

// Correct usage:
shared_ptr<SDL_Surface> s(createSurface(IMG_Load("image.png")));

// Memory Leak:
shared_ptr<SDL_Surface> s(IMG_Load("image.png"));
Run Code Online (Sandbox Code Playgroud)

我想表达的概念是将删除器作为类型的一部分(unique_ptr允许),但具有a的功能shared_ptr.我建议的解决方案是派生自shared_ptr并提供删除器类型作为模板参数.这不会占用额外的内存,并且工作方式与之相同unique_ptr.

template<class T, class D = std::default_delete<T>>
struct shared_ptr_with_deleter : public std::shared_ptr<T>
{
  explicit shared_ptr_with_deleter(T* t = nullptr)
      : std::shared_ptr<T>(t, D()) {}

  // Reset function, as it also needs to properly set the deleter.
  void reset(T* t = nullptr) { std::shared_ptr<T>::reset(t, D());  }
};
Run Code Online (Sandbox Code Playgroud)

与删除类一起(感谢Jonathan Wakely.比我的宏更清洁(现已删除)):

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<SDL_Surface, SDL_Deleter>;
using SurfaceShPtr = shared_ptr_with_deleter<SDL_Surface, SDL_Deleter>;

using RWopsPtr = std::unique_ptr<SDL_RWops, SDL_Deleter>;
using RWopsShPtr = shared_ptr_with_deleter<SDL_RWops, SDL_Deleter>;
Run Code Online (Sandbox Code Playgroud)

具有SurfaceShPtr成员的实例类型保证正确清理,与我相同SurfacePtr,这就是我想要的.

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

// Still correct usage
s.reset(IMG_Load("other.png"));
Run Code Online (Sandbox Code Playgroud)

我会留下一段时间,评论等等,而不接受答案.也许有更多危险的警告我错过了(非虚拟析构函数不是一个,因为父母shared_ptr负责删除).