简体   繁体   English

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

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

I have read through many questions on SO on custom deleter for shared_ptr and unique_ptr , and the difference between the two. 我已经阅读了关于shared_ptrunique_ptr自定义删除器的SO上的许多问题,以及两者之间的区别。 But, I still haven't found any clear answer to this question: 但是,我仍然没有找到这个问题的任何明确答案:

How can one best go about creating a type that acts as a shared_ptr with a custom deleter, similar to how unique_ptr has the deleter as part of the type definition? 如何最好地创建一个充当自定义删除器的shared_ptr的类型,类似于unique_ptr如何将删除器作为类型定义的一部分?

For unique_ptr usage, I use a deleter class, that handles deletion of individual types (limiting it to just two types, for brevity): 对于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>;

Which can be used with something like 哪个可以用于类似的东西

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

And will call SDL_FreeSurface upon destruction. 并在销毁时调用SDL_FreeSurface


This is all fine and well. 这一切都很好。 However, how does one go about achieving the same for shared_ptr ? 但是,如何为shared_ptr实现相同的目标呢? Its type is defined as 其类型定义为

template< class T > class shared_ptr;

and the way to provide a custom deleter is through the constructor. 并且提供自定义删除器的方法是通过构造函数。 It doesn't feel right that the user of the shared_ptr wrapper needs to know which pointer type is wrapped, and how that pointer is supposed to be deleted. shared_ptr包装器的用户需要知道包装了哪个指针类型以及应该如何删除该指针,这感觉不对。 What would be the best way to achieve the same kind of usage as with the unique_ptr example of above. 实现与上述unique_ptr示例相同类型的使用的最佳方法是什么。

In other words, that I could end up with: 换句话说,我最终可能会:

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

Instead of of something like 而不是像

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

Or, just slightly better 或者,稍微好一些

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

Is there a way to do this, without having to create a RAII wrapper class (instead of a typedef), adding even more overhead? 有没有办法做到这一点,而不必创建RAII包装类(而不是typedef),增加更多的开销?


If the answer is "this isn't possible". 如果答案是“这是不可能的”。 Why not? 为什么不?

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

The answer given was that a deleter defined as a type trait was not possible for std::shared_ptr . 给出的答案是std::shared_ptr无法定义为类型特征的删除器。 The answer suggested as an alternative, to use a function which returns a unique_ptr , implicitly converted to a shared_ptr . 答案建议作为替代方案,使用一个返回unique_ptr的函数,隐式转换为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<SDL_Surface> s(createSurface(IMG_Load("image.png")));

// Memory Leak:
shared_ptr<SDL_Surface> 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 . 我想表达的概念是将删除器作为类型的一部分( unique_ptr允许),但具有shared_ptr的功能。 My suggested solution is deriving from shared_ptr , and providing the deleter type as a template argument. 我建议的解决方案是从shared_ptr派生,并提供删除器类型作为模板参数。 This takes up no additional memory, and works in the same way as for unique_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());  }
};

Together with a deleter class (Thanks Jonathan Wakely. Way cleaner than my macro (now removed)): 与删除类一起(感谢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>;

Instances with SurfaceShPtr members are type guaranteed to clean up properly, the same as for SurfacePtr , which is what I wanted. 具有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"));

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). 也许有更多危险的警告我错过了(非虚拟析构函数不是一个,因为父shared_ptr负责删除)。

A typedef is a static, compile-time feature. typedef是一个静态的编译时功能。

A deleter passed to a shared_ptr is a dynamic, run-time property. 传递给shared_ptr删除器是动态的运行时属性。 The deleter is "type-erased" and is not part of the shared_ptr interface. 删除器是“类型擦除的”,不是shared_ptr接口的一部分。

Therefore you can't declare a typedef to represent an alternative deleter, you just pass one to the constructor. 因此,您无法声明typedef来表示替代删除器,只需将其传递给构造函数即可。

What would be the best way to achieve the same kind of usage as with the unique_ptr example of above. 实现与上述unique_ptr示例相同类型的使用的最佳方法是什么。

You could use functions to create the resources and return them in a shared_ptr 您可以使用函数来创建资源并将其返回到shared_ptr

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

But I would have those functions return a unique_ptr instead, which can be converted to shared_ptr , as below. 但我会让这些函数返回一个unique_ptr ,它可以转换为shared_ptr ,如下所示。

I would get rid of the macro and do something like this: 我会摆脱宏并做这样的事情:

// 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.

To answer the shared_ptr part of your question, define functions that create resources and return them in a SDL_ptr : 要回答问题的shared_ptr部分,请定义创建资源的函数并将其返回到SDL_ptr

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

Then you can easily create a shared_ptr from the result of those functions: 然后,您可以从这些函数的结果轻松创建shared_ptr

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

The shared_ptr automatically acquires the right deleter from the unique_ptr . shared_ptr自动从unique_ptr获取正确的删除器。

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

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