2011-11-25 13 views
55

N3290 std::unique_ptr के अनुसार अपने कन्स्ट्रक्टर में एक विलुप्त तर्क स्वीकार करता है।ठीक है, std :: unique_ptr का कस्टम डिलीटर कैसे काम करता है?

हालांकि, मैं विंडोज़ में विजुअल सी ++ 10.0 या मिनजीडब्ल्यू जी ++ 4.4.1 के साथ काम करने के लिए नहीं मिल सकता, न ही उबंटू में जी ++ 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); 

और जीसीसी 4.5 पर में मेरे लिए काम करता है, here

मैं मानक के लिए जा रहा छोड़ देंगे, जब तक आप नहीं लगता है कि उदाहरण वास्तव में क्या कर रही है कि क्या आप ' डी यह करने की उम्मीद है।

+0

जीसीसी लिंक टूटा हुआ है, क्या कोई कोड को फिर से भर सकता है? 'जीसीसी' में क्या अंतर है? – alfC

+1

@alfC: कोई फर्क नहीं पड़ता। मेरे उत्तर में दिखाए गए यह वही कोड है। लिंक संकलन और चलने वाले कोड का सिर्फ एक ऑनलाइन प्रदर्शन था। मैंने इसे अपडेट किया है। –

+2

क्यों न केवल "std :: unique_ptr px (&x);"? – Jon

6

यह काम करता है। विनाश ठीक से होता है।

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 वस्तु के लिए Deleter, अर्थात याद होगा, कि Base एक आभासी नाशक की आवश्यकता नहीं होगी। वह गलत था। मैं Kerrek SB's comment को "उत्तर" के रूप में चुनूंगा, सिवाय इसके कि कोई टिप्पणी के लिए ऐसा नहीं कर सकता है।

@Howard:

#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 को ट्रैक करने के लिए यह उपयोगी होता है, ताकि उस स्मार्ट पॉइंटर से बातचीत करने वाले अन्य कोड बिंदुओं को संकलन समय पर आवंटन/विध्वंस नीति के बारे में जानने की आवश्यकता न हो। इस संदर्भ में, यह जानकारी छिपाने और कोड डुप्लिकेशन (डीआरवाई) को कम करने वाली जानकारी का एक रूप है। –

23

पूरक करने के लिए: कोड के नीचे प्राप्त करने के लिए मैं क्या विश्वास किया एक गतिशील रूप से आवंटित Deleter की लागत का मतलब है कि unique_ptr बॉक्स से बाहर का समर्थन किया था एक ही रास्ता दिखाता है सभी पिछले उत्तरों, अनन्य_प्टर हस्ताक्षर को बिना किसी फंक्शन पॉइंटर या इसके समकक्ष कुछ समकक्ष "प्रदूषित" किए बिना कस्टम डिलीटर करने का एक तरीका है:

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

यह इस तरह, std :: 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

विशेषता एसटीडी टेम्पलेट्स कानूनी और नहीं एक बुरी बात है, लेकिन देखते हैं "नियम" यदि आप का पालन करें [इस पोस्ट] (देखें http://stackoverflow.com/questions/8513417/what-can-and-cant-i करने की जरूरत है -specialize-इन-द-एसटीडी-नाम स्थान)। std :: default_delete टेम्पलेट विशेषज्ञता के लिए एकदम सही उम्मीदवार है। –

+0

शायद की तरह कुछ: वर्ग BIGNUM_ptr: सार्वजनिक std :: unique_ptr { \t BIGNUM_ptr (bignum ख): std :: unique_ptr (बी, और :: बीएन_फ्री); }; हालांकि मुझे अभी तक काम करने के लिए यह नहीं मिला है, और फिर मैं कक्षा को एक टेम्पलेट बनाना चाहता हूं ... –

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^