2013-07-15 11 views
14

इस से इसे जारी रखते हुए: Is the destructor called when a delegating constructor throws?क्या एक प्रतिनिधि कन्स्ट्रक्टर फेंकता है जब स्मृति स्वचालित रूप से पुनः दावा किया जाता है?

class X 
{ 
public: 
    X()  {}; 
    X(int) : X() { throw std::exception(); } 
    X(double)  { throw std::exception(); } 
    ~X(); 
}; 

क्या गतिशील स्मृति के बारे में? आम तौर पर कन्स्ट्रक्टर में एक अपवाद का मतलब है कि वस्तु पूरी तरह से निर्मित नहीं हुई थी और इस प्रकार स्मृति साफ हो गई और खो नहीं गई।

लेकिन पिछले प्रश्न में तर्क यह है कि प्रतिनिधि पूरी होने के बाद ऑब्जेक्ट पूरी तरह से निर्मित (या पूरी तरह से प्रारंभ किया गया) है। यह स्मृति को पुनः प्राप्त करने पर कैसे प्रभाव डालता है? मुझे आशा है कि स्मृति अभी भी साफ हो गई है!

int main() 
{ 
    new X(5);  // new called 
        // delete called because delegate completed. 
        // I assume: 
        //  Memory re-claimed (because constructor did not complete) 
        //  I assume the C++11 standard adjusted to compensate. 
        //  As a constructor did complete. 
} 

भी तुलना: स्मृति, साफ है

int main() 
{ 
    new X(5.0);  // new called 
        // Delete **NOT** called 
        // Memory re-claimed (because constructor did not complete) 
} 

तो जब स्मृति सफाई की परिभाषा ही सी ++ 03 कल्पना से बदल जाना चाहिए। व्यवहार कैसे बदलता है?

+2

मानक कहता है कि "अगर किसी ऑब्जेक्ट के लिए गैर-प्रतिनिधि कन्स्ट्रक्टर निष्पादन पूरा कर चुका है और उस ऑब्जेक्ट के लिए एक प्रतिनिधि कन्स्ट्रक्टर अपवाद के साथ निकलता है, ऑब्जेक्ट के विनाशक को बुलाया जाएगा। " तो विनाशक आह्वान किया जाता है। यदि वह आपके प्रश्न का उत्तर नहीं देता है, तो यह कम से कम मेरे लिए अपर्याप्त रूप से स्पष्ट है। –

+1

@ डेविडस्वार्टज़: विनाश नहीं किया गया लेकिन पुनः दावा किया गया। सी ++ 03 में कन्स्ट्रक्टर में एक अपवाद रिसाव नहीं करता है, क्योंकि कन्स्ट्रक्टर से बचने वाला अपवाद ऑब्जेक्ट को पूरी तरह से गठित नहीं करता है। लेकिन यहां वस्तु पूरी तरह से बनाई गई है। मेरा सवाल वास्तव में सी ++ 03 से हैंडलिंग कैसे बदल गया है। –

+0

@ डेविडस्वार्टज़: आपको इसे एक उत्तर के रूप में पोस्ट करना चाहिए (आदर्श रूप से संदर्भ के साथ)। मैं देखता हूं कि थोड़ी खतरनाक चीज के रूप में ... –

उत्तर

21

यदि new द्वारा बुलाया गया कन्स्ट्रक्टर अपवाद फेंकता है तो new द्वारा आवंटित स्मृति स्वचालित रूप से हटा दी जाती है। प्रतिनिधिमंडल इस संबंध में कुछ भी नहीं बदलते हैं।

एक अपवाद और एक उपयुक्त आवंटन रद्द समारोह फेंक कर समाप्त ऊपर वर्णित वस्तु आरंभीकरण के किसी भी भाग पाया जा सकता है, तो आवंटन रद्द समारोह स्मृति है जिसमें वस्तु का निर्माण किया जा रहा था

मुक्त करने के लिए कहा जाता है

                                                                                                                                                                                                              — सी ++ 11 [expr.new] 5.3.4/18

'वस्तु आरंभीकरण के किसी भी भाग' वर्णित दोनों शामिल कन्स्ट्रक्टर को भेजे गए अभिव्यक्तियों का कन्स्ट्रक्टर कॉल और मूल्यांकन।

इसके अलावा, यह व्यवहार सी ++ 98 मानक [सी ++ 98 5.4.3/17] में समान रूप से निर्दिष्ट किया गया है। रचनाकारों को प्रस्तुत करने वाला एकमात्र अंतर यह है कि यदि आपका मानसिक मॉडल पहले पूरी तरह से निर्मित वस्तु पर आधारित था या नहीं। प्रतिनिधिमंडल को देखते हुए जो अब विलुप्त होने के वास्तविक विनिर्देश के बराबर नहीं है।


अपने पहले उदाहरण में:

new X(5); 

घटनाओं का क्रम है:

  • आवंटन समारोह कहा जाता
  • एक्स (int) कहा जाता
    • एक्स () कहा जाता है (और सफलतापूर्वक बाहर निकलता है)
    • एक्स (पूर्णांक) एक अपवाद
    • ~ एक्स()
  • एक्स (int) कहा जाता अपवाद के माध्यम से बाहर निकल जाता है
  • आवंटन रद्द करने की वजह से कहा जाता समारोह फेंकता ऑब्जेक्ट प्रारंभिक विफल रहा
  • अपवाद conti Nues प्रचार करने के लिए सामान्य रूप से

दूसरे उदाहरण के साथ

new X(5.0); 
  • आवंटन समारोह
  • एक्स (डबल) कहा जाता
  • एक्स (डबल) एक अपवाद के साथ विफल
  • आवंटन रद्द समारोह कहा जाता है कहा जाता है क्योंकि ऑब्जेक्ट प्रारंभिक विफल रहा
  • अपवाद conti Nues प्रचार करने के लिए सामान्य रूप से

आप आवंटन और आवंटन रद्द करने कार्यों बदल कर भी इस व्यवहार का निरीक्षण कर सकते हैं:

#include <iostream> 
#include <cstdlib> 
#include <stdexcept> 
#include <new> 

void *operator new(std::size_t s) { 
    if (void *ptr = std::malloc(s)) { 
     std::cout << "allocation\n"; 
     return ptr; 
    } 
    throw std::bad_alloc{}; 
} 

void operator delete(void *ptr) noexcept { 
    if (ptr) { 
     std::cout << "deallocation\n"; 
     std::free(ptr); 
    } 
} 

struct S { 
    S() {}; 
    S(int) : S{} { throw std::exception(); } 
    S(double) { throw std::exception(); } 
    ~S() { std::cout << "destructor\n"; } 
}; 

int main() { 
    std::cout << "test 1\n"; 
    try { 
     new S(1); 
    } catch(...) { 
     std::cout << "exception caught\n"; 
    } 

    std::cout << "test 2\n"; 
    try { 
     new S(1.); 
    } catch(...) { 
     std::cout << "exception caught\n"; 
    } 
} 

इस कार्यक्रम का सही उत्पादन होता है:

test 1 
allocation 
destructor 
deallocation 
exception caught 
test 2 
allocation 
deallocation 
exception caught 
+1

हां। मुझे लगता है कि कुंजी यह है कि प्राथमिक कन्स्ट्रक्टर से लौटना प्रारंभिकरण का अंत _not_ नहीं है। –

+0

आपके संपादन ने कुछ छोड़ दिया। 'एक्स एक्स (5)' में, चूंकि 'एक्स :: एक्स()' सफल हुआ, 'एक्स :: ~ एक्स() 'को डीलोकेशन से पहले बुलाया जाएगा। –

+0

@BenVoigt जोड़ा गया – bames53

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