2013-04-08 12 views
5

मैं एक स्मार्ट व्यायामकर्ताओं के लिए अभ्यास के रूप में कोड लिख रहा हूं। ऑनलाइन ट्यूटोरियल का उपयोग करना (1, 2) मैंने संदर्भ गणना के साथ एक सामान्य स्मार्ट-पॉइंटर वर्ग विकसित किया है। ,गैर वर्चुअल विनाशकों के लिए ऑब्जेक्ट स्लाइसिंग से बचें

जब स्मार्ट सूचक पता लगाता है कि कोई और अधिक संदर्भ एक विशेष वस्तु के लिए मौजूद हैं, यह वस्तु एक सूचक के माध्यम से मूल प्रकार को हटाना होगा, भले ही: समस्या मैं निम्नलिखित यह पता लगाने में असमर्थ हूँ है अंतिम स्मार्ट पॉइंटर का टेम्पलेट तर्क मूल प्रकार का है। यह गैर-वर्चुअल विनाशकों के लिए ऑब्जेक्ट स्लाइसिंग से बचने के लिए है।

मैं इसे कैसे प्राप्त कर सकता हूं। असल में मेरा कोड नीचे जैसा दिखता है (ट्यूटोरियल से)।

template < typename T > class SP 
{ 
private: 
    T* pData;  // pointer 
    RC* reference; // Reference count 

public: 
    SP() : pData(0), reference(0) 
    { 
     // Create a new reference 
     reference = new RC(); 
     // Increment the reference count 
     reference->AddRef(); 
    } 

    SP(T* pValue) : pData(pValue), reference(0) 
    { 
     // Create a new reference 
     reference = new RC(); 
     // Increment the reference count 
     reference->AddRef(); 
    } 

    SP(const SP<T>& sp) : pData(sp.pData), reference(sp.reference) 
    { 
     // Copy constructor 
     // Copy the data and reference pointer 
     // and increment the reference count 
     reference->AddRef(); 
    } 

    ~SP() 
    { 
     // Destructor 
     // Decrement the reference count 
     // if reference become zero delete the data 
     if(reference->Release() == 0) 
     { 
      delete pData; 
      delete reference; 
     } 
    } 

    T& operator*() 
    { 
     return *pData; 
    } 

    T* operator->() 
    { 
     return pData; 
    } 

    SP<T>& operator = (const SP<T>& sp) 
    { 
     // Assignment operator 
     if (this != &sp) // Avoid self assignment 
     { 
      // Decrement the old reference count 
      // if reference become zero delete the old data 
      if(reference->Release() == 0) 
      { 
       delete pData; 
       delete reference; 
      } 

      // Copy the data and reference pointer 
      // and increment the reference count 
      pData = sp.pData; 
      reference = sp.reference; 
      reference->AddRef(); 
     } 
     return *this; 
    } 
}; 

संपादित करें:

प्राप्त करने के लिए मैं मूल प्रकार के लिए सूचक है करने के लिए है।

मैं एक सवाल यहाँ पोस्ट किया है: delete via a pointer to Derived, not Base

लेकिन अब टिप्पणियों और जवाब को देखने के बाद मुझे लगता है कि दोनों से संबंधित हैं।

template <typename T> 
template <typename U> 
Sptr<T>::Sptr(U* u) : obj(u),ref(NULL) { 
    //do something 
    ref = new RC(); 
    ref->AddRef(); 
} 

अब Sptr<Base1> sp(new Derived); जहां DerivedBase1 से ली गई है पर विचार: मैं निर्माता की है। बेस 1 ने कन्स्ट्रक्टर/विनाशक की रक्षा की है। जो T के किसी ऑब्जेक्ट के लिए संग्रहीत है, लेकिन मुझे इसे टाइप यू के ऑब्जेक्ट के माध्यम से स्टोर करने की आवश्यकता है। मुझे इसे संरक्षित करने की आवश्यकता है। मैं उसे कैसे कर सकता हूँ?

+0

यदि किसी बेस क्लास में वर्चुअल विनाशक नहीं है, और कोई उस बेस क्लास के पॉइंटर के माध्यम से व्युत्पन्न क्लास को हटाने का प्रयास कर रहा है, तो कोई इसे गलत कर रहा है। – Chad

+2

इसे प्राप्त करने के लिए, आपको 'एसपी' एक टेम्पलेट कन्स्ट्रक्टर 'एसपी :: एसपी (यू * यू) {...} 'देना होगा और किसी भी तरह मूल प्रकार' यू' (जिसे' टी') बाद में 'यू' के विनाशक को कॉल करने में सक्षम होने के लिए। – Angew

+0

क्या सी ++ 11 राज्य है कि एक अनुपालन स्मार्ट सूचक यह करना चाहिए? ऐसा प्रतीत होता है कि 'std :: unique_ptr' नहीं है: http://ideone.com/iyanmY – Chad

उत्तर

6

आपके स्मार्ट सूचक को जानकारी के 3 हिस्से की आवश्यकता है।

सबसे पहले, डेटा के सूचक (T* या कुछ)।

दूसरा, आपका संदर्भ संख्या: std::atomic<int> या कुछ।

तीसरा, आपका विनाश समारोह (std::function<void(T*)> या कुछ)।

जब स्मार्ट पॉइंटर पहली बार बनाया गया है, तो विनाश समारोह बनाया गया है। जब आपका स्मार्ट पॉइंटर किसी अन्य स्मार्ट पॉइंटर पर कॉपी किया जाता है, तो यह विनाश कार्य कॉपी किया जाता है। यदि नया स्मार्ट पॉइंटर का प्रकार पुराना से मेल नहीं खाता है, तो विनाश समारोह एक प्रकार के संगत तरीके से लपेटा जाता है (क्या std::function<void(Base*)> = std::function<void(Derived*)> बॉक्स से बाहर काम करता है? भले ही आप मूल रूप से ऐसा कर रहे हों)।

डिफ़ॉल्ट रूप से, यह विनाश कार्य केवल delete t है, लेकिन एक साइड लाभ के रूप में, यह आपके स्मार्ट सूचक के उपयोगकर्ताओं को विनाश समारोह में पारित करने की अनुमति देता है, जो हमेशा delete t नहीं होता है।

Amusingly, reset के समतुल्य पर, आप अपने विनाश समारोह को प्रतिस्थापित करते हैं। तो आप वास्तव में विनाश समारोह का हस्ताक्षर std::function<void()> बना सकते हैं, जो इसे T और U के बीच ले जाने में स्मार्ट पॉइंटर्स को आसान बनाता है।

template < typename T > class SP 
{ 
private: 
    T* pData;  // pointer 
    RC* reference; // Reference count 
    std::function<void()> destroyData; 
public: 
    template<typename U> 
    SP(U* pValue): 
    pData(pValue), 
    reference(nullptr), 
    // store how to destroy pValue now, for later execution: 
    destroyData([pValue]()->void{ 
     delete pValue; 
    }) 
    { 
    // Create a new reference 
    reference = new RC(); 
    // Increment the reference count 
    reference->AddRef(); 
    } 
    // similar for operator=, and you may have to do something for SP<T> as well: 
    template<typename U> 
    SP(const SP<U>& sp): 
    pData(sp.pData), 
    reference(sp.reference), 
    destroyData(sp.destroyData) 
    { 
    // Copy constructor 
    // Copy the data and reference pointer 
    // and increment the reference count 
    reference->AddRef(); 
    } 
    template<typename U> 
    SP<T>& operator = (const SP<U>& sp) 
    { 
    // blah blah blah, then 
    destroyData = sp.destroyData; 
    } 

    ~SP() 
    { 
    // Destructor 
    // Decrement the reference count 
    // if reference become zero delete the data 
    if(reference->Release() == 0) 
    { 
     delete reference; 
     destroyData(); // here I destroyed it! 
    } 
    } 
}; 

या कि

+0

मैं सही विनाश समारोह में पॉइंटर कैसे स्टोर कर सकता हूं। क्या आप थोड़ा और विशिष्ट हो सकते हैं क्योंकि मैं टेम्पलेट कोडिंग की इस शैली में नया हूं। – footy

+0

कोड प्रदर्शन तकनीक के @footy स्निपेट शामिल थे। मैंने निर्माण के लिए गैर-'टी * पॉइंटर्स से निपटने की शुरुआत भी शामिल की, और गैर-'SP ' 'SP ' को निर्दिष्ट प्रकार। यह पूरा नहीं है, लेकिन मुझे उम्मीद है कि आपको विचार मिल जाएगा। एक अच्छी चीज जिसके बारे में आप सोच सकते हैं, अंततः अधिभार रिज़ॉल्यूशन के बिंदु पर 'टी' से निकलने वाले वैध' यू' प्रकार बनाने के लिए SFINAE का उपयोग करना है, लेकिन यह एक उन्नत तकनीक है। – Yakk

+0

धन्यवाद इसने मेरा जीवन बचाया: पी – footy

0

एक वैकल्पिक दृष्टिकोण विलोपन स्मार्ट सूचक में अब एक और वर्ग

// non templated base 
class DeleterBase { 
    public: 
     virtual ~DeleterBase() { }; 
}; 

template <typename T> 
class Deleter : public DeleterBase { 
    private: 
     T *ptr; 
    public: 
     Deleter(T *p) // remember the pointer with the correct type here 
      : ptr{p} 
     { } 

     ~Deleter() { 
      delete this->ptr; // invokes correct destructor 
     } 
}; 

सौंपने शामिल है की तरह कुछ:

template <typename T> 
class SP { 
    private: 
     T *p; 
     DeleterBase *deleter; 
    public: 
     template <typename U> // U is deduced to actual type 
     explicit SP(U *p) 
      : p{p}, 
      deleter{new Deleter<U>(p)} // correct type 
     { } 

     // transfer control in the move constructor/assignment 
     // increase the ref count in copy constructor/assignment 

     // now just delete the deleter in the dtor 
     ~SP() { 
      if (reference_count_is_zero()) { // however you implement this 
       delete this->deleter; 
      } 
     } 
}; 

चूंकि आपके पास पहले से ही आरसी कक्षा है, इसलिए आप उस वर्ग में दिखाए गए कार्यक्षमता को स्थानांतरित कर सकते हैं। उस वर्ग को गैर-टेम्पलेट वाले आधार के साथ टेम्पलेट करें, और पॉइंटर को नष्ट करें जब संदर्भ गणना हटा दी जाती है। चारों ओर गुजरने के लिए एक कम चीज़।

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