2013-03-26 13 views
8

के लिए एक आधार वर्ग तकनीक GotW#8 का कार्य C++ में एक अपवाद के तटस्थ सामान्य ढेर डेटा संरचना को लागू करने, यह मानते हुए केवल टेम्पलेट तर्क नाशक फेंक नहीं करता है। यह चाल संभावित रूप से टेम्पलेट तर्क संचालन (कन्स्ट्रक्टर, कॉपी कन्स्ट्रक्टर, असाइनमेंट) को फेंकने के लिए एक स्थिर स्थिति में स्टैक छोड़ने के तरीके को संभावित रूप से फेंकने के लिए है।अपवाद हैंडलिंग

समाधान में, हर्ब Sutter

इस समाधान सरल रखने के लिए कहते हैं, मैं अपवाद-सुरक्षित संसाधन स्वामित्व के लिए आधार वर्ग तकनीक का प्रदर्शन नहीं करने का फैसला किया है।

कुछ Googling के बाद, मैं द्वारा डेव इब्राहीम 1997 से किए गए उनके समाधान में उन्होंने आवंटन और आधार वर्ग में स्मृति का विलोपन संभालती है, और उपवर्ग में ढेर संचालन लागू करता this answer पाया। इस तरह, वह प्रतिलिपि निर्माता में उस तत्व नकल, स्मृति आवंटन से अलग किया जाता है ताकि यदि नकल विफल रहता है, आधार वर्ग नाशक, कहा जाता है कोई बात नहीं क्या करता है।

संदर्भ के लिए, मेरी टिप्पणी के साथ डेव प्रतिलिपि निर्माता जोड़ा है:

// v_ refers to the internal array storing the stack elements 
Stack(const Stack& rhs) 
     : StackBase<T>(rhs.Count()) // constructor allocates enough space 
             // destructor calls delete[] appropriately 
{ 
     while (Count() < rhs.Count()) 
      Push(rhs.v_[ Count() ]); // may throw 
} 

आधार निर्माता सफल होती है, आधार नाशक में स्मृति सफाई की गारंटी है, भले ही उपवर्ग के प्रति निर्माता फेंकता है।

मेरे प्रश्न हैं:

  1. वहाँ दृष्टिकोण के लिए किसी भी अन्य लाभ है, जैसा कि ऊपर उल्लिखित को छोड़कर?
  2. मैं जब मैं अपने दम पर समस्या हल इस प्रति-निर्माता के साथ आया था:

    // v_ refers to the internal array storing the stack elements 
    // vsize_ is the amount of space allocated in v_ 
    // vused_ is the amount of space used so far in v_ 
    Stack (const Stack &rhs) : 
         vsize_ (0), vused_ (0), v_ (0) { 
        Stack temp (rhs.vused_); // constructor calls `new T[num_elements]` 
              // destructor calls `delete[] v_` 
        std::copy (rhs.v_, rhs.v_ + rhs.vused_, temp.v_); // may throw 
        swap (temp); 
    } 
    void swap (Stack &rhs) { 
        std::swap (v_, rhs.v_); 
        std::swap (vused_, rhs.vused_); 
        std::swap (vsize_, rhs.vsize_); 
    } 
    

    मैं इसे कुछ बोझिल एक आधार वर्ग के लिए जब इस दृष्टिकोण की तुलना में पाते हैं। क्या इस टेम्प-कॉपी-फिर-स्वैप दृष्टिकोण पर बेस-क्लास तकनीक को प्राथमिकता दी जानी चाहिए? ध्यान दें कि दोनों डेव और मैं पहले से ही swap() सदस्य है क्योंकि हम अपने operator=() में इसका इस्तेमाल करते हैं।

  3. डेव इब्राहीम 'तकनीक बहुत अच्छी तरह से (गूगल के अनुसार) नाम से जाना प्रतीत नहीं होता। क्या इसका कोई अलग नाम है, क्या यह मानक अभ्यास है, क्या मुझे कुछ याद आया है?

नोट्स:

  • मान डेव Push() एक पाश में std::copy
  • की मेरी उपयोग के बराबर होने का के स्मार्ट संकेत दिए गए जवाब से बाहर रखने, के रूप में उनके उपयोग दूर के प्रबंधन की बात ले जाएगा जाने इस अभ्यास में स्पष्ट रूप से स्मृति

उत्तर

1

व्यवहारिक रूप से दो कार्यान्वयन समान हैं। वे दोनों एक प्रबंधित स्मृति आवंटन ऑब्जेक्ट सेट करते हैं जो कन्स्ट्रक्टर विफल होने पर स्कोप निकास पर सफाई करेगा। एक अस्थायी चर में कॉपी करना अधिक महंगा हो सकता है, लेकिन टिप्पणियों में उल्लेख किया गया है, std::move शायद ऐसी अतिरिक्त लागतों को रद्द कर देगा। आपके विशिष्ट प्रश्नों के उत्तर में:

  1. अब्राहम द्वारा उदाहरण आपके वास्तविक वर्ग कार्यान्वयन विवरण से आगे ढेर आवंटन को आगे बढ़ाता है।अपने कोड में, यदि आप और अधिक जटिल स्मृति हेरफेर कर से पहले/अपने सरणी कॉपी करने के बाद यह थोड़ा और अधिक मुश्किल सभी संस्थाओं का सही प्रबंधन सुनिश्चित करने के लिए हो सकता है। अन्यथा मुझे पहले कार्यान्वयन के व्यवहार के संबंध में शैली से परे पहले से कवर नहीं किया गया कोई स्पष्ट विवरण नहीं दिख रहा है।
  2. अब्राहम का कार्यान्वयन अमूर्त करता है एक ही स्थान के लिए सफाई। कई वर्गों StackBase<T> का उपयोग करते हैं तो वे एक सुरक्षित रूप से ग्रहण कर सकते हैं उनके गतिशील स्मृति साफ किया जाएगा यदि वे एक अपवाद फेंक देते हैं। अपने कार्यान्वयन में आप अस्थायी वस्तु और स्वैप कोड (कम से कम भागों में) एक ही प्राप्त करने के लिए फिर से लिखने की आवश्यकता होगी। प्रभावी रूप से, यह कार्यान्वयन स्टैकबेस के एकाधिक उप-वर्गों को लागू करने के लिए लाइनों की संख्या को कम कर देता है। हालांकि, यदि आप किसी अन्य बेस क्लास के शीर्ष पर अतिरिक्त मेमोरी आवंटन चाहते हैं तो आपका कार्यान्वयन एकाधिक विरासत से बचाता है। आपका कोड भी बचा जाता है टेम्पलेट कोड सूजन संकलन समय/आकार - हालांकि मैं आम तौर पर इस पर ध्यान दिए बिना नहीं मानते ज्यादातर मामलों में एक बड़ी नकारात्मक होने के लिए। मैं शायद डिफ़ॉल्ट रूप से आपके कोड के करीब कुछ उपयोग करूँगा जब तक कि मैं बहुत सामान्य उपयोग केस कोड लिखने की कोशिश नहीं कर रहा था।
  3. मुझे नहीं पता कि इस दृष्टिकोण का एक विशिष्ट नाम है - अगर मुझे कोई मिल जाए तो मैं इसे अपडेट कर दूंगा - लेकिन मैंने इसे कम से कम एक सी ++ प्रोग्रामिंग पुस्तक में पहले देखा है।
+0

आधार साझा करना आधार वर्ग दृष्टिकोण के पक्ष में एकमात्र तर्क प्रतीत होता है। मैं इसे देखने के लिए एक या दो दिन दूंगा कि कोई और तर्क के साथ आता है या नहीं। – Irfy

+0

बेस वर्ग दृष्टिकोण जीतता है जब ऑब्जेक्ट की सामग्री भारी हैं और 'temp' साथ अदला-बदली महंगा है। (हालांकि सी ++ 11 की 'चाल' मदद कर सकती है) यदि आप चाहें तो आप इसे अपने उत्तर में जोड़ सकते हैं। – Irfy

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