繁体   English   中英

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

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

我已经阅读了关于shared_ptrunique_ptr自定义删除器的SO上的许多问题,以及两者之间的区别。 但是,我仍然没有找到这个问题的任何明确答案:

如何最好地创建一个充当自定义删除器的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>;

哪个可以用于类似的东西

SurfacePtr surface(IMG_Load("image.png"));

并在销毁时调用SDL_FreeSurface


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

template< class T > class shared_ptr;

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

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

SurfaceShPtr surface(IMG_Load("image.png"));

而不是像

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

或者,稍微好一些

SurfaceShPtr surface(IMG_Load("image.png"),
                     SDL_Deleter());

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


如果答案是“这是不可能的”。 为什么不?

这里提供的另一个答案是,通过带有自定义删除器的unique_ptr函数返回可以完成我所要求的接近,可以隐式转换为shared_ptr

给出的答案是std::shared_ptr无法定义为类型特征的删除器。 答案建议作为替代方案,使用一个返回unique_ptr的函数,隐式转换为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"));

我想表达的概念是将删除器作为类型的一部分( unique_ptr允许),但具有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());  }
};

与删除类一起(感谢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>;

具有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"));

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

typedef是一个静态的编译时功能。

传递给shared_ptr删除器是动态的运行时属性。 删除器是“类型擦除的”,不是shared_ptr接口的一部分。

因此,您无法声明typedef来表示替代删除器,只需将其传递给构造函数即可。

实现与上述unique_ptr示例相同类型的使用的最佳方法是什么。

您可以使用函数来创建资源并将其返回到shared_ptr

shared_ptr<SDL_Surface> create_sdl_surface(const char* s)
{
  return shared_ptr<SDL_Surface>(IMG_load(s), SDL_FreeSurface);
}

但我会让这些函数返回一个unique_ptr ,它可以转换为shared_ptr ,如下所示。

我会摆脱宏并做这样的事情:

// type with overloaded functions for freeing each resource type
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); }
  // etc.
};

// a unique_ptr using SDL_deleter:
template<typename P>
  using SDL_Ptr = std::unique_ptr<P, SDL_deleter>;

// typedefs for the common ptr types:
using SurfacePtr = SDL_ptr<SDL_Surface>;
using RWopsPtr = SDL_ptr<SDL_RWops>;
// etc.

要回答问题的shared_ptr部分,请定义创建资源的函数并将其返回到SDL_ptr

SurfacePtr createSurface(const char* s) { return SurfacePtr(IMG_load(s)); }
RWopsPtr createRWops([...]) { return RWopsPtr([...]); }
// etc.

然后,您可以从这些函数的结果轻松创建shared_ptr

shared_ptr<SDL_Surface> s = createSurface("image.png");

shared_ptr自动从unique_ptr获取正确的删除器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM