[英]Using an abstract deleter with std::unique_ptr
我想要一個提供一些創建方法的運行時接口。 這些方法返回unique_ptr<T>
,我想通過創建 class 來啟用自定義刪除。 問題是我絕對不希望接口直接提供這些方法——它們應該只在破壞unique_ptr<T, SomeCustomDel>
。 現在,我想我可以使用std::unique_ptr<T, std::function<void(T*)>>
,但我真的不想這樣做,因為我根本不需要那種抽象級別而且我不需要不想支付堆分配。
有什么建議么?
您的規范對我來說並不完全清楚,但是您是否考慮過unique_ptr<T, void(*)(void*)>
? 這是一種非常靈活的類型,具有動態刪除器的許多特性。
如果這不是您想要的,您可以嘗試以下方式:
class impl
{
public:
virtual ~impl();
virtual void operator()(void*) = 0;
virtual void other_functionality() = 0;
};
class my_deleter
{
impl* p_;
public:
...
void operator()(void* p) {(*p_)(p);}
void other_functionality() {p_->other_functionality();}
...
};
如果沒有關於您的要求的更多詳細信息,很難知道什么是最適合您的情況。
我希望有std::unique_ptr
的標准“動態”刪除器版本。 這個神話般的 class 將允許我在實例化它時將刪除器附加到 unique_ptr ,類似於std::shared_ptr
。
也就是說,如果存在這樣的類型,我懷疑它基本上是用std::unique_ptr<T,std::function<void(T*)>>
實現的。 你想避免的事情。
但是我認為您低估std::function
。 它的實現是優化以避免在可能的情況下碰到堆。 如果你的刪除器 object 仍然很小,那么一切都將在堆棧上完成(我認為boost::function
可以靜態處理最大 32 字節的刪除器)。
A 用於刪除器過於籠統的問題。 您必須提供刪除器的定義。 沒有辦法解決這個問題。 但是,您不必讓用戶實例化 class,這實際上禁止他們使用它。 為此,刪除器的構造函數需要一個僅在實現文件中定義的標記結構。
或者可能是最簡單的解決方案。 將刪除器放在詳細命名空間中。 用戶仍然可以自由使用它,但很明顯,當您更改它時,他們不應該也不能抱怨,破壞了他們的代碼。
我看到兩個選項。
選項 1:如果需要,使用包含 function 指針和可選的原始字符數組的自定義刪除器對一些 state 進行編碼:
template<class T>
void simply_delete(T* ptr, const unsigned char*) {
delete ptr;
}
template<class T, int StateSize>
struct my_deleter {
void (*funptr)(T*,const unsigned char*);
array<unsigned char,StateSize> state;
my_deleter() : funptr(&simply_delete<T>) {}
void operator()(T* ptr) const {
funptr(ptr,StateSize>0 ? &state[0] : nullptr);
}
};
template<class T>
using upi = unique_ptr<T,my_deleter<T,sizeof(void*)>>;
現在,您可以創建不同的upi<T>
對象來存儲不同的 function 指針和刪除器狀態,而無需提及其類型中究竟發生了什么。 但這與實現“小型 function 優化”的function<>
刪除器幾乎相同。 您可以期望一個體面的標准庫實現為不需要任何堆分配的小型 function 對象(如 function 指針)提供非常有效的function<>
包裝器。 至少我會。 :)
選項 2:簡單地使用 shared_ptr 而不是 unique_ptr 並利用其內置的類型擦除功能來處理刪除器。 這也將允許您輕松支持 Derived->Base 轉換。 為了最大程度地控制分配的內容,您可以使用 std::allocate_shared function 模板。
這是對其中一個答案的回應,而不是對原始問題的回應。 僅僅因為格式原因,它是一個答案而不是評論。
我希望有
std::unique_ptr
的標准“動態”刪除器版本。 這個神話般的 class 將允許我在實例化它時將刪除器附加到unique_ptr
,類似於std::shared_ptr
。
這是實現此類 class 的開始。 這很容易做到。 我僅將unique_ptr
用作異常安全輔助,僅此而已。 它並不像您希望的那樣功能齊全。 這些額外的功能留給讀者作為練習。 :-) 下面的內容為自定義動態刪除器建立了指針和存儲的唯一所有權。 請注意,即使智能指針的構造函數拋出異常,智能指針也擁有傳入的指針(這實際上是unique_ptr
在實現中最有用的地方)。
#include <memory>
#include <type_traits>
namespace detail
{
class impl
{
public:
virtual ~impl() {};
};
template <class T, class D>
class erase_type
: public impl
{
T* t_;
D d_;
public:
explicit erase_type(T* t)
noexcept(std::is_nothrow_default_constructible<D>::value)
: t_(t)
{}
erase_type(T* t, const D& d)
noexcept(std::is_nothrow_copy_constructible<D>::value)
: t_(t),
d_(d)
{}
erase_type(T* t, D&& d)
noexcept(std::is_nothrow_move_constructible<D>::value)
: t_(t),
d_(std::move(d))
{}
virtual ~erase_type()
{
if (t_)
d_(t_);
}
erase_type(const erase_type&) = delete;
erase_type& operator=(const erase_type&) = delete;
};
} // detail
template <class T>
class my_pointer
{
T* ptr_;
detail::impl* impl_;
public:
my_pointer() noexcept
: ptr_(nullptr),
impl_(nullptr)
{}
template <class Y>
explicit my_pointer(Y* p)
: ptr_(static_cast<T*>(p)),
impl_(nullptr)
{
std::unique_ptr<Y> hold(p);
impl_ = new detail::erase_type<Y, std::default_delete<Y>>(p);
hold.release();
}
template <class Y, class D>
explicit my_pointer(Y* p, D&& d)
: ptr_(static_cast<T*>(p)),
impl_(nullptr)
{
std::unique_ptr<Y, D&> hold(p, d);
typedef
detail::erase_type<Y, typename std::remove_reference<D>::type>
ErasedType;
impl_ = new ErasedType(p, std::forward<D>(d));
hold.release();
}
~my_pointer()
{
delete impl_;
}
my_pointer(my_pointer&& p) noexcept
: ptr_(p.ptr_),
impl_(p.impl_)
{
p.ptr_ = nullptr;
p.impl_ = nullptr;
}
my_pointer& operator=(my_pointer&& p) noexcept
{
delete impl_;
ptr_ = p.ptr_;
impl_ = p.impl_;
p.ptr_ = nullptr;
p.impl_ = nullptr;
return *this;
}
typename std::add_lvalue_reference<T>::type
operator*() const noexcept
{return *ptr_;}
T* operator->() const noexcept
{return ptr_;}
};
請注意,與unique_ptr
(和shared_ptr
一樣)不同,采用指針的構造函數不是noexcept
。 盡管可以通過使用“小型刪除器”優化來緩解這種情況。 還有一個練習留給讀者。 :-)
我發現這個問題在谷歌上搜索我自己的問題; 使用帶有抽象基 class 指針的 unique_ptr。 所有的答案都很棒。 我發現@deft_code 最適合我的需求。
這就是我最終在我的情況下所做的:
假設 T 是一個抽象基 class 類型。 makeT(), func 創建一些派生的 class 的新實例,並返回 T*:
std::unique_ptr<T, std::function<void(T*)>> p(makeT(), [](T* p){p.delete();});
我只是想與那些正在尋找簡短、復制和粘貼解決方案的人分享這個。
對於未經訓練的 c++11 眼睛,[](... 語法是 lambda。
正如其他答案中提到的那樣,它不是 C 意義上的“函數指針”,而是可調用的 c++ object,它真的很小,應該有足夠的開銷來攜帶它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.