2013-08-28 8 views
5
class XX 
{ 
public: 
    static unsigned s_cnt; 
    XX() 
    { 
     ++ s_cnt; 
     std::cout << "C XX " << s_cnt << "\n"; 

     if (s_cnt > 2) 
      throw std::exception(); 
    } 

    //private: 
    ~XX() 
    { 
     std::cout << "~ XX\n"; 
    } 
}; 
unsigned XX::s_cnt = 0; 

int main() 
{ 
    try 
    { 
     XX *xx = new XX[10]; 
    } catch (...) 
    { 
     std::cout << "Exc\n"; 
    } 
} 

आउटपुट:सरणी निर्माण के दौरान सी ++ में एक अपरिचित अपवाद फेंकने वाले विनाशकों को क्यों नहीं बुलाया जाता है?

C XX 1 
C XX 2 
C XX 3 
~ XX 
~ XX 
Exc 

लेकिन जब मैं कोशिश पकड़ निकालने के लिए, मैं देख:

C XX 1 
C XX 2 
C XX 3 
terminate called after throwing an instance of 'std::exception' 
    what(): std::exception 
zsh: abort  ./a.out 

क्यों सी ++ पहले मामले में विनाशकर्ता फोन है, लेकिन दूसरे में नहीं है?

उत्तर

13

जब आपको कोई अपवाद नहीं मिलता है (यानी यह एक अपरिचित अपवाद बन जाता है और आपके प्रोग्राम को समाप्त करता है), सी ++ आपको कोई गारंटी नहीं देता है कि विनाशकों को वास्तव में बुलाया जाता है।

यह अपवाद हैंडलिंग को कार्यान्वित करने के तरीके में कंपाइलर्स को छूट देता है। उदाहरण के लिए, जीसीसी पहले एक हैंडलर की खोज करता है। यदि यह एक नहीं मिल पा रहा है, तो यह तुरंत बंद हो जाता है, डीबगिंग के लिए पूर्ण स्टैक जानकारी को संरक्षित करता है। यदि यह एक पाता है, तो यह वास्तव में स्टैण्ड को खोलता है, वस्तुओं को नष्ट कर देता है, जब तक यह हैंडलर पर नहीं आता है। यही कारण है कि आप आउटपुट नहीं देखते हैं: प्रोग्राम किसी ऑब्जेक्ट को नष्ट करने से पहले बंद कर देता है।

+1

आप जोड़ सकते हैं कि इसका कारण यह है कि आप कोर डंप में राज्य की जानकारी को खो नहीं देते हैं। –

+0

अच्छा बिंदु, जोड़ा गया। –

3

जब आप किसी निर्माता से अपवाद फेंकते हैं, तो मानक के रूप में ऑब्जेक्ट का इलाज नहीं बनाया गया है। आप ऐसा कुछ नहीं नष्ट करते जो अस्तित्व में नहीं है, है ना?

वास्तव में, यहां तक ​​कि इस सरल उदाहरण आप का सुझाव के रूप में काम नहीं करता है:

struct A { 
    A() {throw std::exception();} 
    ~A() {std::cout << "A::~A()" << std::endl;} 
}; 

int main() { 
    A a; 
} 

यह न आया हुआ अपवाद के साथ समाप्त हो जाता है, लेकिन प्रिंट नहीं करता "एक :: ~ एक()"।

यदि आप इसके बारे में सोचते हैं, तो यह एक अर्थपूर्ण तरीका देने का एकमात्र संभव तरीका है।

उदाहरण के लिए, हम एक वर्ग बी के एक सदस्य वस्तु के रूप में हमारे ग्रुप ए का उपयोग कर सकते हैं:

struct B { 
    B() : m_a(),m_p(new int) {} 
    ~B() {delete m_p;} 

    A m_a; 
    int * m_p; 
}; 

जाहिर B::B() तुरंत फेंकता है (और यहां तक ​​m_p प्रारंभ नहीं है)। यदि इस मामले में B::~B() पर कॉल करने के लिए एक काल्पनिक सी ++ मानक अनिवार्य है, तो विनाशक को यह जानने का कोई संभावित तरीका नहीं है कि m_p प्रारंभ किया गया है या नहीं।

दूसरे शब्दों में, एक निर्माता से अपवाद फेंकने का अर्थ है कि ऑब्जेक्ट कभी अस्तित्व में नहीं था और इसका जीवनकाल कभी भी शुरू नहीं हुआ। यह GotW इसके बारे में बहुत स्पष्ट है।

बोनस: B की परिभाषा में क्या होता है यदि हम m_a और m_p के आदेश को स्वैप करते हैं? फिर आपके पास मेमोरी रिसाव है। यही कारण है कि सदस्य वस्तुओं के प्रारंभ के दौरान अपवाद पकड़ने के लिए एक विशेष वाक्यविन्यास मौजूद है।

+0

नया एक्सएक्स [10] वस्तुओं को एक-एक करके बनाता है। पहली 2 वस्तुओं को बिना किसी समस्या के बनाया गया है। फिर यह तीसरे और विफल होने का प्रयास करता है। फिर यह एक्सएक्स [1] और एक्सएक्स [0] के लिए विनाशक को बुलाता है, लेकिन एक्सएक्स [2] नहीं। वास्तव में आपके पास आपके आउटपुट में केवल 2 ~ ~ XX' लाइनें हैं, न कि 3. – sbabbi

+0

मैं दूसरे मामले का जिक्र कर रहा था, जहां 'कोशिश' ब्लॉक हटा दिया गया है और वस्तुओं के निर्माण के बावजूद कोई विनाशक नहीं कहा जाता है। आप प्रश्न के उस हिस्से को संबोधित नहीं कर रहे हैं। – juanchopanza

+0

वैसे मैं उस संपादन को याद करता हूं जहां आपने प्रश्न शीर्षक बदल दिया, मेरा बुरा। दूसरे भाग को @ सेबेस्टियन रेडल उत्तर द्वारा संबोधित किया गया है। लेकिन इस तथ्य से कोई लेना-देना नहीं है कि आप सरणी के साथ काम कर रहे हैं। – sbabbi

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