我已经通过对定制删除这么多的问题读取shared_ptr
和unique_ptr
,以及两者之间的差异。但是,对于这个问题,我仍然没有找到任何明确的答案:
如何最好地创建一个shared_ptr
与自定义删除器一起使用的类型,类似于unique_ptr
该删除器如何作为类型定义的一部分?
为了unique_ptr
使用,我使用一个deleteer类,该类处理单个类型的删除(为简便起见,仅将其限制为两种类型):
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
。建议的答案是,使用返回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"));
我要表达的概念是将Deleter作为类型的一部分(unique_ptr
允许),但是具有a的功能shared_ptr
。我建议的解决方案是从派生的shared_ptr
,并提供deleter类型作为模板参数。这不会占用额外的内存,并且与的工作方式相同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
成员的实例具有保证正确清理的类型,这与for相同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
负责删除)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句