2011-06-12 49 views
5

我想要一個提供一些創建方法的運行時接口。這些方法返回unique_ptr<T>,我想通過創建類來啓用自定義刪除。問題是我絕對不希望界面直接提供這些方法 - 它們應該只在銷燬unique_ptr<T, SomeCustomDel>時可用。現在,我想我可以使用std::unique_ptr<T, std::function<void(T*)>>,但我真的不想因爲我根本不需要這種抽象級別,而且我也不想支付堆分配。在std :: unique_ptr中使用抽象刪除器

有什麼建議嗎?

+0

定製刪除器版本的'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

+0

@Kerrek SB:該點不是'unique_ptr'的開銷,它是'std :: function'的額外開銷。 – Puppy 2011-06-13 18:22:25

回答

2

您的規格對我而言並不完全清楚,但您是否考慮過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();} 
    ... 
}; 

這是很難知道什麼是最好的,你的情況沒有了解您的需求更多的細節。

+0

我最終以模板形式與此一起。 – Puppy 2011-06-13 18:35:10

+0

我沒有看到第二部分是如何改善'std :: function '作爲刪除者的。 – sellibitze 2011-06-14 08:37:01

+0

@sellibitze:因爲,一個很好的模板將清理那個可怕的類型 - 不安全的'void *',並且'impl'可以直接指向分配類。因爲'unique_ptr'是在另一邊創建的,所以我不需要在主分配類中公開這個接口,這正是我想要的。 – Puppy 2011-06-14 09:42:33

1

我看到兩個選項。

方法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函數模板的地方。

2

我希望有一個標準的「動態」刪除版本的std::unique_ptr。當我實例化這個神話類時,我會將刪除器附加到unique_ptr,類似於std::shared_ptr

這就是說,如果這種類型存在,我懷疑它實質上將與std::unique_ptr<T,std::function<void(T*)>>實施。你想避免的事情。我想你會低估std::function。它的實現是儘可能避免碰到堆的優化。如果你的刪除器對象很小,一切都將在堆棧中完成(我認爲boost::function可以靜態處理最多32字節的刪除器)。

A代表過於普遍的刪除問題。您必須提供刪除者的定義。這是沒有辦法的。但是,您不必讓用戶實例化該類,這基本上禁止他們使用它。爲此,刪除者的構造函數需要一個只在實現文件中定義的標籤結構。

或者可能是最簡單的解決方案。將刪除器放在詳細的命名空間中。用戶仍然可以自由使用它,但很顯然,當你改變它時,他們不應該也不會抱怨,破壞他們的代碼。

1

這是對其中一個答案的迴應,而不是原始問題。僅僅是因爲格式化原因,它是一個回答而不是評論。

我希望能有一個標準的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。儘管可以通過使用「小型刪除者」優化來緩解這種情況。還有另一個練習留給讀者。 :-)

0

我發現這個問題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++對象,它實際上很小,並且應該具有可忽略的開銷來攜帶它。

相關問題