簡體   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