2011-11-25 60 views
55

根據N3290 std::unique_ptr在其構造函數中接受deleter參數。那麼,std :: unique_ptr的定製刪除器如何工作?

但是,我不能在Windows中使用Visual C++ 10.0或MinGW g ++ 4.4.1,也不能在Ubuntu中使用g ++ 4.6.1。

因此,我害怕我對它的理解是不完整或錯誤的,我無法看到明顯被忽略的刪除參數,所以任何人都可以提供一個工作示例?

最好我想看看它是如何工作的unique_ptr<Base> p = unique_ptr<Derived>(new Derived)

可能使用標準中的一些措辭來備份示例,即使用您正在使用的任何編譯器,它實際上做了它應該做的事情?

回答

45

這對我的作品在MSVC10

int x = 5; 
auto del = [](int * p) { std::cout << "Deleting x, value is : " << *p; }; 
std::unique_ptr<int, decltype(del)> px(&x, del); 

而且在GCC 4.5,here

我將跳過要標準,除非你不認爲例子是做什麼你」 d期待它做到。

+0

gcc鏈接被破壞,有人可以重新編碼嗎? 'gcc'有什麼區別? – alfC

+1

@alfC:沒有區別。這與我的答案中顯示的代碼完全相同。鏈接是代碼編譯和運行的只是一個在線演示。我已經更新了它。 –

+2

爲什麼不只是「std :: unique_ptr px(&x);」? – Jon

6

This Works。破壞正常發生。

class Base 
{ 
    public: 
    Base() { std::cout << "Base::Base\n"; } 
    virtual ~Base() { std::cout << "Base::~Base\n"; } 
}; 


class Derived : public Base 
{ 
    public: 
    Derived() { std::cout << "Derived::Derived\n"; } 
    virtual ~Derived() { std::cout << "Derived::~Derived\n"; } 
}; 

void Delete(const Base* bp) 
{ 
    delete bp; 
} 

int main() 
{ 
    std::unique_ptr<Base, void(*)(const Base*)> ptr = std::unique_ptr<Derived, void(*)(const Base*)>(new Derived(), Delete); 
} 
10

我的問題已經很好地回答了。

但是爲了以防萬一人不知道,我有誤解,認爲一個unique_ptr<Derived>可以移動到unique_ptr<Base>,然後就記住了Derived對象缺失者,即是Base不會需要有一個虛析構函數。那是錯誤的。我選擇Kerrek SB's comment作爲「答案」,除了一個人不能這樣評論。

@Howard:下面的代碼說明了實現什麼,我相信一個動態分配的缺失者的成本不得不意味着unique_ptr支持開箱即用的方式之一:

#include <iostream> 
#include <memory>   // std::unique_ptr 
#include <functional>  // function 
#include <utility>   // move 
#include <string> 
using namespace std; 

class Base 
{ 
public: 
    Base() { cout << "Base:<init>" << endl; } 
    ~Base() { cout << "Base::<destroy>" << endl; } 
    virtual string message() const { return "Message from Base!"; } 
}; 

class Derived 
    : public Base 
{ 
public: 
    Derived() { cout << "Derived::<init>" << endl; } 
    ~Derived() { cout << "Derived::<destroy>" << endl; } 
    virtual string message() const { return "Message from Derived!"; } 
}; 

class BoundDeleter 
{ 
private: 
    typedef void (*DeleteFunc)(void* p); 

    DeleteFunc deleteFunc_; 
    void*  pObject_; 

    template< class Type > 
    static void deleteFuncImpl(void* p) 
    { 
     delete static_cast< Type* >(p); 
    } 

public: 
    template< class Type > 
    BoundDeleter(Type* pObject) 
     : deleteFunc_(&deleteFuncImpl<Type>) 
     , pObject_(pObject) 
    {} 

    BoundDeleter(BoundDeleter&& other) 
     : deleteFunc_(move(other.deleteFunc_)) 
     , pObject_(move(other.pObject_)) 
    {} 

    void operator() (void*) const 
    { 
     deleteFunc_(pObject_); 
    } 
}; 

template< class Type > 
class SafeCleanupUniquePtr 
    : protected unique_ptr< Type, BoundDeleter > 
{ 
public: 
    typedef unique_ptr< Type, BoundDeleter > Base; 

    using Base::operator->; 
    using Base::operator*; 

    template< class ActualType > 
    SafeCleanupUniquePtr(ActualType* p) 
     : Base(p, BoundDeleter(p)) 
    {} 

    template< class Other > 
    SafeCleanupUniquePtr(SafeCleanupUniquePtr<Other>&& other) 
     : Base(move(other)) 
    {} 
}; 

int main() 
{ 
    SafeCleanupUniquePtr<Base> p(new Derived); 
    cout << p->message() << endl; 
} 

乾杯,

+1

你能解釋一下使用它必須具有一個非虛擬析構函數的類,但是從它派生並希望獲得通過基指針調用的派生析構函數?這是否違背虛擬析構函數的常識? – stijn

+3

@stijn:只要其他一些機制(如自定義刪除器)完成識別最派生類的工作,析構函數在技術上並不需要是虛擬的。然後使其成爲非虛擬的一個有效原因是保持與外部施加的存儲器佈局的兼容性,即不希望在存儲器區域的前面存在vtable ptr。另一個有效的原因是,如果銷燬通常是通過刪除器進行的,那麼使析構器虛擬化會錯誤地指示可以使用C++'delete',或者至少有一些原因。 –

+0

@stjn另一個原因是你使用多種內存分配/釋放技術,你只希望創建點知道該特定實例的分配策略/技術。因此跟蹤相應正確的dtor和智能指針是非常有用的,這樣與智能指針交互的其他代碼點在編譯時就不需要知道分配/解除分配策略。在這種情況下,它是一種信息隱藏和減少代碼重複(DRY)的形式。 –

23

爲了補充以前所有的答案,沒有辦法有一個定製刪除,而無需通過具有或者函數指針或等價的東西在裏面像這種「污染」了的unique_ptr簽名:

std::unique_ptr< MyType, myTypeDeleter > // not pretty 

這是通過提供專業化的標準:: default_delete模板類,像這樣實現的:

namespace std 
{ 
template<> 
class default_delete<MyType> 
{ 
public: 
    void operator()(MyType *ptr) 
    { 
    delete ptr; 
    } 
}; 
} 

現在所有std::unique_ptr<MyType>說,「看到」這種專業化將隨之被刪除。請注意,它可能不是您想要的所有std::unique_ptr<MyType>,所以請仔細選擇您的解決方案。

+0

是不是在名稱空間std壞習慣中編寫代碼? – odinthenerd

+1

專業std模板是合法的,不是一個壞習慣,但有一些「規則」,你需要遵循,請參閱[本文](http://stackoverflow.com/questions/8513417/what-c​​an-and-cant-i -specialize合的-STD-命名空間)。 std :: default_delete是模板專業化的完美人選。 –

+0

也許類似: 類BIGNUM_ptr:公共的std ::的unique_ptr { \t BIGNUM_ptr(BIGNUM B):標準::的unique_ptr (b,&:: BN_free); }; 雖然我還沒有得到它的工作,然後我想要使這個類的模板... –

相關問題