2015-10-28 8 views
63

कृपया निम्न अपवाद फेंकने पर एक नज़र डालें और पकड़ने:खतरनाक संदर्भ द्वारा अपवाद को पकड़ रहा है?

void some_function() { 
    throw std::exception("some error message"); 
} 

int main(int argc, char **argv) { 
    try { 
     some_function(); 
    } catch (const std::exception& e) { 
     std::cerr << e.what() << std::endl; 
     exit(1); 
    } 
    return 0; 
} 

यह संदर्भ द्वारा फेंका अपवाद को पकड़ने के लिए सुरक्षित है?

मेरी चिंता का विषय है क्योंकि अपवाद e वास्तव में ढेर some_function() की पर रखा गया है। लेकिन some_function() अभी वापस आ गया है, जिससे e को नष्ट किया जा सकता है। तो वास्तव में अब एक विनाशकारी वस्तु के लिए e अंक।

क्या मेरी चिंता सही है?

अपवाद को मूल्य के बिना कॉपी किए बिना सही तरीका क्या है? क्या मुझे new std::exception() फेंकना चाहिए ताकि इसे गतिशील स्मृति में रखा जा सके?

+7

ध्यान दें कि std :: अपवाद के लिए स्ट्रिंग कंस्ट्रक्टर मानक नहीं है, यह एक एमएस (अनुरूप?) एक्सटेंशन है। http://stackoverflow.com/questions/5157206/why-does-stdexception-have-extra-constructors-in-vc – Sigismondo

+0

तो आप अपवाद को संदेश कैसे असाइन कर सकते हैं, इसलिए इसे 'what() 'का उपयोग करके मुद्रित किया जाता है? – SomethingSomething

+4

@ कुछ ऐसा जो आप 'std :: अपवाद 'के व्युत्पन्न का उपयोग करेंगे जो कि निर्माता को संदेश पास करने की अनुमति देता है। उदाहरण के लिए 'std :: runtime_error'। और उपप्रकार फेंकने का सम्मेलन बिल्कुल कारण है कि आपको (कॉन्स) संदर्भ क्यों पकड़ना चाहिए। – user2079303

उत्तर

93

यह वास्तव में सुरक्षित है।

"e वास्तव में some_function() के ढेर पर रखा गया है"

कोई यह नहीं है ...वास्तव में फेंक दिया वस्तु स्मृति की एक अनिर्दिष्ट क्षेत्र अपवाद संचालन तंत्र द्वारा उपयोग के लिए आरक्षित में बन जाता है:

[except.throw] 15.1/4: अपवाद वस्तु के लिए स्मृति एक अनिर्दिष्ट में आवंटित किया जाता है जैसा कि 3.7.4.1 में उल्लेख किया गया है। अपवाद ऑब्जेक्ट को नष्ट करने के अलावा किसी अन्य माध्यम से अपवाद के लिए अंतिम शेष सक्रिय हैंडलर के बाद नष्ट हो गया है, या प्रकार की अंतिम वस्तु std :: upgrade_ptr (18.8.5) जो अपवाद वस्तु को संदर्भित करती है, जो भी नष्ट हो जाती है, जो भी हो बाद में है।

एक स्थानीय चर throw को निर्दिष्ट किया जाता है, तो यह की नकल की है वहाँ करने के लिए यदि आवश्यक हो तो (अनुकूलक सीधे इस अन्य स्मृति में बनाने में सक्षम हो सकता है)। इसलिए ...

15,1/5 जब फेंका वस्तु एक वर्ग वस्तु है, निर्माता कॉपी-प्रारंभ और नाशक के लिए चुने गए सुलभ होंगे, भले ही कॉपी/कदम आपरेशन elided है (12.8)।


हैं कि उन पर क्लिक नहीं है, यह पराक्रम मदद कार्यान्वयन थोड़ा इस तरह की कल्पना करना:

// implementation support variable... 
thread__local alignas(alignof(std::max_align_t)) 
    char __exception_object[EXCEPTION_OBJECT_BUFFER_SIZE]; 

void some_function() { 
    // throw std::exception("some error message"); 

    // IMPLEMENTATION PSEUDO-CODE: 
    auto&& thrown = std::exception("some error message"); 
    // copy-initialise __exception_object... 
    new (&__exception_object) decltype(thrown){ thrown }; 
    throw __type_of(thrown); 
    // as stack unwinds, _type_of value in register or another 
    // thread_local var... 
} 

int main(int argc, char **argv) 
{ 
    try { 
     some_function(); 
    } // IMPLEMENTATION: 
     // if thrown __type_of for std::exception or derived... 
     catch (const std::exception& e) { 
     // IMPLEMENTATION: 
     // e references *(std::exception*)(&__exception_object[0]); 
     ... 
    } 
} 
+0

मुझे लगता है कि 15.1.3 उतना ही प्रासंगिक है जितना अधिक नहीं। विशेष रूप से "* एक अपवाद प्रतिलिपि बनाना (8.5, 12.8) एक अस्थायी वस्तु [...] *" शुरू होता है, इसलिए, ई 'कभी भी ऑब्जेक्ट को फेंकने वाली वस्तु को इंगित नहीं करता है, लेकिन एक प्रतिलिपि - अपवाद ऑब्जेक्ट - जिसे प्रारंभ करने के लिए उपयोग किया गया था 'पकड़' का तर्क। – luk32

+1

@ luk32: मुझे विश्वास नहीं है: महत्वपूर्ण बात यह है कि अपवाद ऑब्जेक्ट का जीवनकाल प्रासंगिक 'पकड़' कथन (ओं) को वापस अनदेखा करता है - उस पर 15.1.3 का असर क्या है? फिर भी, अगर आप पहले से नहीं हैं, तो सिग्सिमोदो के जवाब को ऊपर उठाने के लिए स्वतंत्र महसूस करें। 15.1.3 के अपने स्वयं के पैराफ्रेशिंग/निष्कर्ष त्रुटिपूर्ण आईएमएचओ हैं - अपवाद ऑब्जेक्ट सृजन को बढ़ाया जा सकता है, इसलिए दो अलग-अलग ऑब्जेक्ट्स की आवश्यकता नहीं है, जैसे कि "ऑब्जेक्ट को फेंकने वाली वस्तु को इंगित न करें, लेकिन एक प्रतिलिपि" को सटीक रूप से बनाया जा सकता है। –

+1

उम्म .. ठीक है, मुझे थोड़ा पीछे हटने दो, यह उतना ही महत्वपूर्ण है। मेरा मुद्दा यह है कि जो कुछ भी फेंक दिया जाता है, वह पकड़ के दायरे में मर सकता है। अर्थात्, समारोह समाप्त हो गया। यदि संकलक निर्णय लेता है कि यह elision प्रदर्शन करना चाहता है यह ठीक है। बात यह है कि * अपवाद ऑब्जेक्ट * वह नहीं है जिसे आप 'फेंक' कथन में पास करते हैं, लेकिन एक * अस्थायी * इससे बनाया गया है। उनका जीवनकाल एक दूसरे से बंधे नहीं है। मैंने दोनों बीटीडब्ल्यू अपडेट किया। – luk32

18

कॉन्स्ट संदर्भ द्वारा पकड़ना बिल्कुल कितना अपवाद पकड़ा जाना चाहिए। अपवाद वस्तु जरूरी नहीं है कि 'स्टैक पर' रहें। संकलक इस काम को करने के लिए उपयुक्त जादू के लिए ज़िम्मेदार है।

दूसरी तरफ, आपका उदाहरण संकलित नहीं हो सकता है क्योंकि std::exception केवल डिफ़ॉल्ट-निर्मित या प्रति-निर्मित हो सकता है। इस मामले में what() विधि एक पॉइंटर को खाली (सी-शैली) स्ट्रिंग पर वापस कर देगी, जो विशेष रूप से उपयोगी नहीं है।

आप एक std::runtime_error या std::logic_error के रूप में उपयुक्त फेंक सुझाएँ, या एक वर्ग उससे व्युत्पन्न:

  • logic_error जब फोन करने वाले अपनी सेवा के डिजाइन मानकों के बाहर कुछ अनुरोध किया है।
  • runtime_error जब कॉलर ने कुछ उचित अनुरोध किया है लेकिन बाहरी कारक आपको अनुरोध का सम्मान करने से रोकते हैं।

http://en.cppreference.com/w/cpp/error/exception

22

आप , संदर्भ द्वारा पकड़ने के लिए है अन्यथा आप संभवतः वस्तु की सही गतिशील प्रकार प्राप्त नहीं कर सका। अपने जीवनकाल, मानक की गारंटी देता है, [except.throw] में,

अपवाद वस्तु किसी भी rethrowing, या प्रकार एसटीडी के अंतिम वस्तु के अलावा अन्य तरह से अपवाद बाहर निकलता है के लिए या तो अंतिम शेष सक्रिय हैंडलर के बाद नष्ट हो जाता है के रूप में :: और सिफारिश की - - const संदर्भ द्वारा पकड़ने के लिए exception_ptr (18.8.5) है कि अपवाद वस्तु को संदर्भित करता है नष्ट हो जाता है, जो भी बाद

+4

ठीक है, आपको * नहीं करना है, ऐसा करना कानूनी नहीं है। हालांकि, अगर आप नहीं करते हैं, तो आप पहले से ही अपने घुटने पर बंदूक का लक्ष्य रख रहे हैं। यदि आप मूल्य से पकड़ते हैं तो आप एक संभावित मध्यवर्ती प्रतिलिपि प्रस्तुत करते हैं और अधिक महत्वपूर्ण रूप से ऑब्जेक्ट स्लाइसिंग करते हैं। – luk32

8

से except.throw:

फेंकने एक अपवाद कॉपी-initializes (8.5, 12.8) एक अस्थायी वस्तु, अपवाद वस्तु कहा जाता है। अस्थायी एक लाभा है और पर मिलान करने वाले हैंडलर (15.3) में घोषित चर को प्रारंभ करने के लिए उपयोग किया जाता है। यदि अपवाद ऑब्जेक्ट का प्रकार अपूर्ण प्रकार या पॉइंटर एक अपूर्ण प्रकार (संभावित रूप से सीवी-योग्य) शून्य के अलावा एक अपूर्ण प्रकार होगा, तो प्रोग्राम खराब हो जाएगा।

यह अपवाद फेंकने का कार्य है जो किसी भी ढेर के बाहर अपवाद-क्षेत्र में अपवाद वस्तु की प्रतिलिपि बनाता है। तो यह संदर्भ द्वारा अपवाद पकड़ने के लिए पूरी तरह से कानूनी और सलाहकार है, क्योंकि अपवाद ऑब्जेक्ट जीवनकाल अंतिम संभव catch() तक विस्तारित होगा।

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