我想要一個提供一些創建方法的運行時接口。這些方法返回unique_ptr<T>
,我想通過創建類來啓用自定義刪除。問題是我絕對不希望界面直接提供這些方法 - 它們應該只在銷燬unique_ptr<T, SomeCustomDel>
時可用。現在,我想我可以使用std::unique_ptr<T, std::function<void(T*)>>
,但我真的不想因爲我根本不需要這種抽象級別,而且我也不想支付堆分配。在std :: unique_ptr中使用抽象刪除器
有什麼建議嗎?
我想要一個提供一些創建方法的運行時接口。這些方法返回unique_ptr<T>
,我想通過創建類來啓用自定義刪除。問題是我絕對不希望界面直接提供這些方法 - 它們應該只在銷燬unique_ptr<T, SomeCustomDel>
時可用。現在,我想我可以使用std::unique_ptr<T, std::function<void(T*)>>
,但我真的不想因爲我根本不需要這種抽象級別,而且我也不想支付堆分配。在std :: unique_ptr中使用抽象刪除器
有什麼建議嗎?
您的規格對我而言並不完全清楚,但您是否考慮過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();}
...
};
這是很難知道什麼是最好的,你的情況沒有了解您的需求更多的細節。
我最終以模板形式與此一起。 – Puppy 2011-06-13 18:35:10
我沒有看到第二部分是如何改善'std :: function
@sellibitze:因爲,一個很好的模板將清理那個可怕的類型 - 不安全的'void *',並且'impl'可以直接指向分配類。因爲'unique_ptr'是在另一邊創建的,所以我不需要在主分配類中公開這個接口,這正是我想要的。 – Puppy 2011-06-14 09:42:33
我看到兩個選項。
方法1:使用包含一個函數指針和可選的原始字符數組一個定製刪除編碼,如果必要的一些狀態:
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<>
。至少我是。 :)
選項2:只需使用shared_ptr而不是unique_ptr,並利用其關於刪除程序的內置類型擦除功能。這也可以讓你輕鬆支持Derived-> Base轉換。爲了最大限度地控制可以使用std :: allocate_shared函數模板的地方。
我希望有一個標準的「動態」刪除版本的std::unique_ptr
。當我實例化這個神話類時,我會將刪除器附加到unique_ptr,類似於std::shared_ptr
。
這就是說,如果這種類型存在,我懷疑它實質上將與std::unique_ptr<T,std::function<void(T*)>>
實施。你想避免的事情。我想你會低估std::function
。它的實現是儘可能避免碰到堆的優化。如果你的刪除器對象很小,一切都將在堆棧中完成(我認爲boost::function
可以靜態處理最多32字節的刪除器)。
A代表過於普遍的刪除問題。您必須提供刪除者的定義。這是沒有辦法的。但是,您不必讓用戶實例化該類,這基本上禁止他們使用它。爲此,刪除者的構造函數需要一個只在實現文件中定義的標籤結構。
或者可能是最簡單的解決方案。將刪除器放在詳細的命名空間中。用戶仍然可以自由使用它,但很顯然,當你改變它時,他們不應該也不會抱怨,破壞他們的代碼。
這是對其中一個答案的迴應,而不是原始問題。僅僅是因爲格式化原因,它是一個回答而不是評論。
我希望能有一個標準的
std::unique_ptr
「動態」 刪除器版本。 這個神話類將允許我 當我實例化時附加刪除器到unique_ptr
,類似於std::shared_ptr
。
這是一個這樣的類的實現的開始。這很容易做到。我只使用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
。儘管可以通過使用「小型刪除者」優化來緩解這種情況。還有另一個練習留給讀者。 :-)
我發現這個問題googling我自己的問題;使用具有抽象基類指針的unique_ptr。所有的答案都很棒。我發現@ deft_code是我最需要的。
這是我落得這樣做在我的情況:
假設T是一個抽象基類。該MAKET(),FUNC帶來了一些派生類的新實例,並返回T *:
std::unique_ptr<T, std::function<void(T*)>> p(makeT(), [](T* p){p.delete();});
我只是想分享這對於那些誰正在尋找短,複製和pasteish解決方案。
對於未經訓練的C++ 11眼,[](...語法是一個lambda。
正如在其他的答案中提到,它不是在C意義上的「函數指針」,但一個可調用的C++對象,它實際上很小,並且應該具有可忽略的開銷來攜帶它。
定製刪除器版本的'unique_ptr'究竟有什麼問題?您確定它比標準刪除器版本?MSVC中'shared_ptr'的實現是[第一個高級STL情節](http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-1 -of-n),看起來'shared_ptr'類的兩個版本都非常類似於t o其內部複雜性。如果返回'unique_ptr'是最乾淨的解決方案,也許您不必擔心其成本太高。 –
2011-06-13 09:46:52
@Kerrek SB:該點不是'unique_ptr'的開銷,它是'std :: function'的額外開銷。 – Puppy 2011-06-13 18:22:25