[英]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_ptr
和unique_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.