क्या आप (वास्तव में) अपवाद सुरक्षित कोड लिखते हैं? [बन्द है]


312

अपवाद हैंडलिंग (ईएच) वर्तमान मानक प्रतीत होता है, और वेब पर खोज करने से, मुझे कोई भी उपन्यास विचार या विधियां नहीं मिल सकती हैं जो इसे सुधारने या बदलने की कोशिश करें (खैर, कुछ बदलाव मौजूद हैं, लेकिन कुछ भी नहीं उपन्यास)।

हालांकि अधिकांश लोग इसे अनदेखा करते हैं या बस इसे स्वीकार करते हैं, ईएच में कुछ बड़ी कमियां हैं: अपवाद कोड के लिए अदृश्य हैं और यह कई, कई संभव निकास बिंदु बनाता है। सॉफ्टवेयर पर जोएल ने इसके बारे में एक लेख लिखा था । gotoसही फिट होने की तुलना में , इसने मुझे EH के बारे में फिर से सोचने पर मजबूर कर दिया।

मैं ईएच से बचने की कोशिश करता हूं और सिर्फ रिटर्न वैल्यू, कॉलबैक या जो भी उद्देश्य पूरा करता है उसका उपयोग करता हूं। लेकिन जब आपको विश्वसनीय कोड लिखना होता है, तो आप इन दिनों केवल ईएच को नजरअंदाज नहीं कर सकते हैं : यह उसी के साथ शुरू होता है new, जो केवल 0 लौटने (पुराने दिनों की तरह) के बजाय एक अपवाद फेंक सकता है। यह C ++ कोड की किसी भी लाइन को अपवाद के रूप में असुरक्षित बनाता है। और फिर C ++ फाउंडेशनल कोड के अपवादों में अधिक स्थान ... std lib यह करता है, और इसी तरह।

यह महसूस होता है कि यह अस्थिर स्थानों पर चल रहा है .. इसलिए, अब हम अपवादों के बारे में ध्यान रखने के लिए मजबूर हैं!

लेकिन यह कठिन है, वास्तव में कठिन है। आपको अपवाद सुरक्षित कोड लिखना सीखना होगा, और यहां तक ​​कि अगर आपके पास इसके साथ कुछ अनुभव है, तो भी सुरक्षित होने के लिए कोड की किसी भी एक पंक्ति को दोबारा जांचना आवश्यक होगा! या आप हर जगह ब्लॉक / कोशिश को पकड़ना शुरू कर देते हैं, जो कोड को तब तक बंद कर देता है जब तक कि यह बिना पढ़े नहीं रहता।

EH ने पुराने स्वच्छ निर्धारक दृष्टिकोण (मान वापस ..) को बदल दिया, जिसमें एक दृष्टिकोण के साथ कुछ ही लेकिन समझने योग्य और आसानी से हल करने योग्य कमियां थीं जो आपके कोड में कई संभव निकास बिंदु बनाता है, और यदि आप कोड लिखना शुरू करते हैं जो अपवादों को पकड़ता है (आप क्या हैं कुछ बिंदु पर करने के लिए मजबूर किया जाता है, तो यह आपके कोड के माध्यम से पथों की एक भीड़ भी बनाता है (कैच ब्लॉकों में कोड, एक सर्वर प्रोग्राम के बारे में सोचें जहां आपको std :: cerr .. के अलावा लॉगिंग सुविधाओं की आवश्यकता है)। ईएच के फायदे हैं, लेकिन यह बात नहीं है।

मेरे वास्तविक प्रश्न:

  • क्या आप वास्तव में अपवाद सुरक्षित कोड लिखते हैं?
  • क्या आप सुनिश्चित हैं कि आपका अंतिम "प्रोडक्शन रेडी" कोड अपवाद सुरक्षित है?
  • क्या आप यह भी सुनिश्चित कर सकते हैं, कि यह क्या है?
  • क्या आप जानते हैं और / या वास्तव में काम करने वाले विकल्पों का उपयोग करते हैं?

44
"ईएच ने पुराने स्वच्छ निर्धारक दृष्टिकोण (वापसी मूल्यों ..) को प्रतिस्थापित किया" क्या? अपवाद रिटर्न कोड के रूप में नियतात्मक हैं। उनके बारे में कुछ भी यादृच्छिक नहीं है।
rlbond 21

2
EH एक लंबे समय के लिए मानक रहा है (जब से Ada भी!)

12
"EH" अपवाद हैंडलिंग के लिए एक स्थापित संक्षिप्त नाम है? मैंने इसे पहले कभी नहीं देखा।
थॉमस पैड्रोन-मैक्कार्थी

3
न्यूटन के नियम आज अधिकांश चीजों के लिए लागू हैं। यह तथ्य कि उन्होंने सदियों से बहुत व्यापक घटनाओं की भविष्यवाणी की है, महत्वपूर्ण है।
डेविड थॉर्नले

4
@frunsi: हे, तुम्हें भ्रमित करने के लिए क्षमा करें! लेकिन मुझे लगता है कि, अगर कुछ भी, एक अजीब और अस्पष्टीकृत संक्षिप्त या संक्षिप्त नाम लोगों को अधिक से अधिक हतोत्साहित करेगा।
थॉमस पैड्रॉन-मैक्कार्थी

जवाबों:


520

आपका प्रश्न यह दावा करता है कि, "अपवाद-सुरक्षित कोड लिखना बहुत कठिन है"। मैं पहले आपके प्रश्नों का उत्तर दूंगा, और फिर, उनके पीछे छिपे प्रश्न का उत्तर दूंगा।

सवालों का जवाब दे

क्या आप वास्तव में अपवाद सुरक्षित कोड लिखते हैं?

बेशक मैं।

यही कारण है कि जावा ने C ++ प्रोग्रामर (RAII शब्दार्थ की कमी) के रूप में मुझसे अपनी अपील को बहुत खो दिया है, लेकिन मैं पचा रहा हूं: यह C ++ प्रश्न है।

यह वास्तव में आवश्यक है, जब आपको एसटीएल या बूस्ट कोड के साथ काम करने की आवश्यकता होती है। उदाहरण के लिए, C ++ थ्रेड्स ( boost::threadया std::thread) इनायत से बाहर निकलने के लिए एक अपवाद फेंक देंगे।

क्या आप सुनिश्चित हैं कि आपका अंतिम "प्रोडक्शन रेडी" कोड अपवाद सुरक्षित है?

क्या आप यह भी सुनिश्चित कर सकते हैं, कि यह क्या है?

अपवाद-सुरक्षित कोड लिखना बग-मुक्त कोड लिखने जैसा है।

आप 100% सुनिश्चित नहीं हो सकते कि आपका कोड अपवाद सुरक्षित है। लेकिन फिर, आप इसके लिए प्रयास करते हैं, अच्छी तरह से ज्ञात पैटर्न का उपयोग करते हैं, और प्रसिद्ध विरोधी पैटर्न से बचते हैं।

क्या आप जानते हैं और / या वास्तव में काम करने वाले विकल्पों का उपयोग करते हैं?

C ++ में कोई व्यवहार्य विकल्प नहीं हैं (यानी आपको C पर वापस लौटना होगा और C ++ लाइब्रेरी से बचना होगा, साथ ही बाहरी आश्चर्य जैसे विंडोज SEH)।

अपवाद सुरक्षित कोड लिखना

अपवाद सुरक्षित कोड लिखने के लिए, आपको पहले पता होना चाहिए कि प्रत्येक निर्देश आपके द्वारा लिखे गए अपवाद सुरक्षा के किस स्तर पर है।

उदाहरण के लिए, एक newएक अपवाद फेंक सकते हैं, लेकिन बताए एक अंतर्निहित (एक पूर्णांक है, या एक सूचक जैसे) असफल नहीं होंगे। एक स्वैप कभी असफल नहीं होगा (कभी फेंकने वाला स्वैप न लिखें), std::list::push_backफेंक सकता है ...

अपवाद की गारंटी

समझने की पहली बात यह है कि आपको अपने सभी कार्यों द्वारा दी गई अपवाद गारंटी का मूल्यांकन करने में सक्षम होना चाहिए:

  1. कोई नहीं : आपके कोड को कभी भी प्रस्ताव नहीं देना चाहिए। यह कोड सब कुछ लीक कर देगा, और पहले फेंके गए अपवाद पर टूट जाएगा।
  2. मूल : यह गारंटी है कि आपको बहुत कम से कम प्रस्ताव देना चाहिए, अर्थात यदि कोई अपवाद फेंका गया है, तो कोई संसाधन लीक नहीं हुए हैं, और सभी वस्तुएं अभी भी पूरी हैं
  3. मजबूत : प्रसंस्करण या तो सफल होगा, या एक अपवाद को फेंक देगा, लेकिन अगर यह फेंकता है, तो डेटा उसी स्थिति में होगा जैसे कि प्रसंस्करण बिल्कुल भी शुरू नहीं हुआ था (यह C ++ को एक लेन-देन की शक्ति देता है)
  4. nothrow / nofail : प्रसंस्करण सफल होगा।

कोड का उदाहरण

निम्न कोड सही C ++ की तरह लगता है, लेकिन सच में, "कोई नहीं" गारंटी प्रदान करता है, और इस प्रकार, यह सही नहीं है:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   X * x = new X() ;                // 2. basic : can throw with new and X constructor
   t.list.push_back(x) ;            // 3. strong : can throw
   x->doSomethingThatCanThrow() ;   // 4. basic : can throw
}

मैं इस तरह के विश्लेषण के साथ अपने सभी कोड लिखता हूं।

प्रदान की गई न्यूनतम गारंटी बुनियादी है, लेकिन फिर, प्रत्येक निर्देश का क्रम पूरे फ़ंक्शन को "कोई नहीं" बनाता है, क्योंकि यदि 3. फेंकता है, तो एक्स लीक हो जाएगा।

पहली बात यह है कि फ़ंक्शन को "बेसिक" बनाना होगा, जो कि एक स्मार्ट पॉइंटर में x डाल रहा है जब तक कि यह सुरक्षित रूप से सूची के स्वामित्व में न हो:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   std::auto_ptr<X> x(new X()) ;    // 2.  basic : can throw with new and X constructor
   X * px = x.get() ;               // 2'. nothrow/nofail
   t.list.push_back(px) ;           // 3.  strong : can throw
   x.release() ;                    // 3'. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 4.  basic : can throw
}

अब, हमारा कोड एक "बुनियादी" गारंटी प्रदान करता है। कुछ भी लीक नहीं होगा, और सभी ऑब्जेक्ट एक सही स्थिति में होंगे। लेकिन हम और अधिक, यानी मजबूत गारंटी दे सकते हैं। यह वह जगह है जहां यह कर सकते हैं महंगा हो जाते हैं, और ऐसा क्यों है न सब सी ++ कोड मजबूत है। चलो यह कोशिश करते हैं:

void doSomething(T & t)
{
   // we create "x"
   std::auto_ptr<X> x(new X()) ;    // 1. basic : can throw with new and X constructor
   X * px = x.get() ;               // 2. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 3. basic : can throw

   // we copy the original container to avoid changing it
   T t2(t) ;                        // 4. strong : can throw with T copy-constructor

   // we put "x" in the copied container
   t2.list.push_back(px) ;          // 5. strong : can throw
   x.release() ;                    // 6. nothrow/nofail
   if(std::numeric_limits<int>::max() > t2.integer)  // 7.   nothrow/nofail
      t2.integer += 1 ;                              // 7'.  nothrow/nofail

   // we swap both containers
   t.swap(t2) ;                     // 8. nothrow/nofail
}

हमने संचालन को फिर से आदेश दिया, पहले Xइसके सही मूल्य पर निर्माण और सेटिंग । यदि कोई ऑपरेशन विफल हो जाता है, तो tसंशोधित नहीं किया जाता है, इसलिए, ऑपरेशन 1 से 3 को "मजबूत" माना जा सकता है: यदि कुछ फेंकता है, tतो संशोधित नहीं किया गया है, और Xरिसाव नहीं होगा क्योंकि यह स्मार्ट पॉइंटर के स्वामित्व में है।

फिर, हम एक प्रतिलिपि बनाने t2के t7. करने के लिए, और ऑपरेशन 4 से इस प्रति पर काम कुछ फेंकता है, तो t2संशोधित किया गया है, लेकिन फिर, tअभी भी मूल है। हम अभी भी मजबूत गारंटी प्रदान करते हैं।

फिर, हम स्वैप tऔर t2। स्वैप ऑपरेशन C ++ में नहीं होना चाहिए, तो चलिए आशा करते हैं कि आपने जो स्वैप लिखा है Tवह nothrow है (यदि ऐसा नहीं है, तो इसे फिर से लिखें क्योंकि यह nothrow है)।

इसलिए, यदि हम फ़ंक्शन के अंत तक पहुंचते हैं, तो सब कुछ सफल हुआ (रिटर्न प्रकार की कोई आवश्यकता नहीं) और tइसका अपवाद मूल्य है। यदि यह विफल रहता है, तो tअभी भी इसका मूल मूल्य है।

अब, मजबूत गारंटी की पेशकश करना काफी महंगा हो सकता है, इसलिए अपने सभी कोड को मजबूत गारंटी की पेशकश करने का प्रयास न करें, लेकिन यदि आप इसे बिना लागत के कर सकते हैं (और C ++ inlining और अन्य अनुकूलन सभी कोड को महंगा बना सकते हैं) , तो यह करो। फ़ंक्शन उपयोगकर्ता इसके लिए आपको धन्यवाद देगा।

निष्कर्ष

अपवाद-सुरक्षित कोड लिखने की कुछ आदतें होती हैं। आपको उपयोग किए जाने वाले प्रत्येक निर्देश द्वारा दी गई गारंटी का मूल्यांकन करने की आवश्यकता होगी, और फिर, आपको निर्देशों की एक सूची द्वारा दी गई गारंटी का मूल्यांकन करने की आवश्यकता होगी।

बेशक, C ++ कंपाइलर गारंटी को वापस नहीं करेगा (मेरे कोड में, मैं गारंटी को @warning doxygen टैग के रूप में प्रस्तुत करता हूं), जो थोड़े दुखद है, लेकिन यह आपको अपवाद-सुरक्षित कोड लिखने की कोशिश करने से नहीं रोकना चाहिए।

सामान्य विफलता बनाम बग

एक प्रोग्रामर कैसे गारंटी दे सकता है कि कोई भी विफल कार्य हमेशा सफल नहीं होगा? आखिरकार, फ़ंक्शन बग हो सकता है।

यह सच है। बग-मुक्त कोड द्वारा अपवाद गारंटी की पेशकश की जानी चाहिए। लेकिन फिर, किसी भी भाषा में, फ़ंक्शन को दबाने से फ़ंक्शन बग मुक्त हो जाता है। बग होने की संभावना के खिलाफ कोई भी संन्यासी कोड खुद को बचाता नहीं है। सबसे अच्छा कोड आप लिख सकते हैं, और फिर, यह बग रहित होने की गारंटी के साथ गारंटी प्रदान करें। और अगर कोई बग है, तो उसे ठीक करें।

अपवाद असाधारण प्रसंस्करण विफलता के लिए हैं, कोड बग के लिए नहीं।

आखरी श्ब्द

अब, सवाल "क्या यह इसके लायक है?"।

निश्चित रूप से यह है। एक "nothrow / no-fail" फ़ंक्शन के होने के कारण यह जानना कि फ़ंक्शन विफल नहीं होगा, एक बड़ा वरदान है। एक "मजबूत" फ़ंक्शन के लिए भी यही कहा जा सकता है, जो आपको डेटाबेस, जैसे कमिट / रोलबैक सुविधाओं के साथ ट्रांजेक्शनल शब्दार्थ के साथ कोड लिखने में सक्षम बनाता है, कमिट कोड का सामान्य निष्पादन है, अपवाद को रोलबैक होने के लिए फेंक देता है।

फिर, "मूल" आपको प्रदान की जाने वाली बहुत कम गारंटी है। C ++ एक बहुत ही मजबूत भाषा है, इसके स्कैप्स के साथ, आपको किसी भी संसाधन लीक से बचने के लिए सक्षम बनाता है (कुछ कचरा संग्रहकर्ता को डेटाबेस, कनेक्शन या फ़ाइल हैंडल के लिए ऑफ़र करना मुश्किल होगा)।

तो, जहाँ तक मैं इसे देखना रूप में, यह है इसके लायक।

2010-01-29 संपादित करें: गैर-फेंकने वाली स्वैप के बारे में

nobar ने एक टिप्पणी की जो मुझे विश्वास है, काफी प्रासंगिक है, क्योंकि यह "आप अपवाद सुरक्षित कोड कैसे लिखते हैं" का एक हिस्सा है:

  • [मुझे] एक स्वैप कभी असफल नहीं होगा (एक फेंक स्वैप भी नहीं लिखें)
  • [nobar] यह कस्टम लिखित swap()कार्यों के लिए एक अच्छी सिफारिश है। हालांकि, यह ध्यान दिया जाना चाहिए std::swap()कि आंतरिक रूप से उपयोग किए जाने वाले संचालन के आधार पर यह विफल हो सकता है

डिफ़ॉल्ट std::swapप्रतियां और कार्य करेगा, जो कुछ वस्तुओं के लिए फेंक सकता है। इस प्रकार, डिफ़ॉल्ट स्वैप फेंक सकता है, या तो आपकी कक्षाओं के लिए या एसटीएल कक्षाओं के लिए भी उपयोग किया जाता है। जहां तक ​​C ++ मानक का सवाल है, स्वैप ऑपरेशन के लिए vector, dequeऔर listनहीं फेंकेंगे, जबकि यह इस बात के लिए हो सकता है mapकि तुलना फ़ेंक्टर कॉपी निर्माण पर फेंक सकता है (देखें C ++ प्रोग्रामिंग लैंग्वेज, स्पेशल एडिशन, परिशिष्ट E, E.4.3 .Swap )।

सदिश की अदला-बदली के विजुअल C ++ 2008 के कार्यान्वयन को देखते हुए, यदि दोनों वैक्टर में एक ही एलोकेटर (यानी सामान्य स्थिति) है, तो वेक्टर की अदला-बदली नहीं होगी, लेकिन अगर उनके पास अलग-अलग एलोकेटर हैं तो वे प्रतियां बना देंगे। और इस प्रकार, मुझे लगता है कि यह इस अंतिम मामले में फेंक सकता है।

इसलिए, मूल पाठ अभी भी धारण करता है: कभी भी फेंकने वाली स्वैप न लिखें, लेकिन नॉबर की टिप्पणी को याद रखना चाहिए: सुनिश्चित करें कि जिन वस्तुओं को आप स्वैप कर रहे हैं, वे एक गैर-फेंकने वाली स्वैप हैं।

2011-11-06 को संपादित करें: दिलचस्प लेख

डेव अब्राहम , जिन्होंने हमें बुनियादी / मजबूत / नोटरी गारंटी दी , एक लेख में एसटीएल अपवाद बनाने के बारे में अपने अनुभव का वर्णन किया:

http://www.boost.org/community/exception_safety.html

7 वें बिंदु को देखें (अपवाद-सुरक्षा के लिए स्वचालित परीक्षण), जहां वह यह सुनिश्चित करने के लिए स्वचालित इकाई परीक्षण पर निर्भर है कि हर मामले का परीक्षण किया जाए। मुझे लगता है कि यह भाग प्रश्न लेखक के " क्या आप भी सुनिश्चित कर सकते हैं, कि यह है? " का एक उत्कृष्ट उत्तर है ।

संपादित करें 2013-05-31: डायनाडार से टिप्पणी

t.integer += 1;गारंटी के बिना है कि अतिप्रवाह सुरक्षित अपवाद नहीं होगा, और वास्तव में तकनीकी रूप से यूबी को लागू कर सकता है! (हस्ताक्षरित अतिप्रवाह UB: C ++ 11 5/4 है "यदि किसी अभिव्यक्ति के मूल्यांकन के दौरान, परिणाम गणितीय रूप से परिभाषित नहीं है या अपने प्रकार के लिए प्रतिनिधित्व योग्य मूल्यों की सीमा में नहीं है, तो व्यवहार अपरिभाषित है।") नोट अहस्ताक्षरित है। पूर्णांक अतिप्रवाह नहीं करते हैं, लेकिन उनकी गणना एक समतुल्य श्रेणी मोडुलो 2 ^ # बिट्स में करते हैं।

डायनाडार निम्नलिखित पंक्ति का उल्लेख कर रहा है, जिसमें वास्तव में अपरिभाषित व्यवहार है।

   t.integer += 1 ;                 // 1. nothrow/nofail

यहाँ समाधान यह सत्यापित करने के लिए है कि पूर्णांक पहले से ही अधिकतम मूल्य पर है (उपयोग करते हुए std::numeric_limits<T>::max()) इसके अतिरिक्त।

मेरी त्रुटि "सामान्य विफलता बनाम बग" अनुभाग में जाएगी, जो कि एक बग है। यह तर्क को अमान्य नहीं करता है, और इसका मतलब यह नहीं है कि अपवाद-सुरक्षित कोड बेकार है क्योंकि इसे प्राप्त करना असंभव है। आप कंप्यूटर को बंद करने, या कंपाइलर बग्स, या यहाँ तक कि अपने बग्स, या अन्य त्रुटियों के खिलाफ खुद की रक्षा नहीं कर सकते। आप पूर्णता प्राप्त नहीं कर सकते हैं, लेकिन आप जितना संभव हो उतना निकट पाने की कोशिश कर सकते हैं।

मैंने डायनाडार की टिप्पणी को ध्यान में रखते हुए कोड को सही किया।


6
आपका बहुत बहुत धन्यवाद! मुझे अब भी कुछ अलग होने की उम्मीद थी। लेकिन मैं आपके जवाब को C ++ से चिपकाकर और आपके विस्तृत विवरण के लिए स्वीकार करता हूं। आपने मेरा "" यह भी अपवाद के लिए असुरक्षित C ++ कोड की किसी भी लाइन के बारे में बनाता है। लाइन विश्लेषण द्वारा यह लाइन आखिर समझ में आती है ... मुझे इसके बारे में सोचना होगा।
फ्रून्सी

8
"एक स्वैप कभी असफल नहीं होगा"। कस्टम-लिखित स्वैप () फ़ंक्शन के लिए यह एक अच्छी सिफारिश है। यह ध्यान दिया जाना चाहिए, हालांकि, यह std :: swap () उन ऑपरेशनों के आधार पर विफल हो सकता है जो इसे आंतरिक रूप से उपयोग करते हैं।
नोबार

7
@ मेहरदाद:: "finally" does exactly what you were trying to achieveबेशक यह करता है। और क्योंकि यह भंगुर कोड का उत्पादन करना आसान है finally, "ट्राय-विथ-रिसोर्स" धारणा अंततः जावा 7 में प्रस्तुत की गई (जो कि C # 's के 10 साल बाद usingऔर C ++ डिस्ट्रक्टर्स के 3 दशक बाद) है। यही वह क्रूरता है जिसकी मैं आलोचना करता हूं। जैसा कि Just because [finally] doesn't match your taste (RAII doesn't match mine, [...]) doesn't mean it's "failing": इसमें, उद्योग आपके स्वाद से असहमत है, क्योंकि गारबेज कलेक्टेड भाषाओं में RAII- प्रेरित कथन (C # 's usingऔर Java try) को जोड़ना है ।
पियरसबल

5
@ मेहरदाद:: RAII doesn't match mine, since it needs a new struct every single darn time, which is tedious sometimesनहीं, यह नहीं है। संसाधनों की "सुरक्षा" करने के लिए आप स्मार्ट पॉइंटर्स या यूटिलिटी क्लासेस का उपयोग कर सकते हैं।
पियरसेबल

5
@ मेहरदाद: "यह आपको कुछ के लिए एक रैपर बनाने के लिए मजबूर करता है, जिसके लिए आपको रैपर की आवश्यकता नहीं हो सकती है" - जब RAII फैशन में कोडिंग करते हैं, तो आपको किसी भी तरह के रैपर की आवश्यकता नहीं होगी: रिसोर्स स्रोत है, और जीवन भर संसाधन है। हालाँकि, मैं सी ++ दुनिया से आता हूं, मैं वर्तमान में जावा प्रोजेक्ट के साथ संघर्ष कर रहा हूं। C ++ में RAII की तुलना में, जावा "ए मेस" में "मैनुअल रिसोर्स मैनेजमेंट" है! मेरी राय, लेकिन जावा ने बहुत पहले "स्वचालित संसाधन mgmt" के लिए "स्वचालित मेमोरी mgmt" का कारोबार किया था।
फ्रांउसी

32

C ++ में अपवाद-सुरक्षित कोड लिखना बहुत सारे उपयोग के बारे में इतना नहीं है {} कैच {} ब्लॉक। यह दस्तावेज के बारे में है कि आपका कोड किस प्रकार की गारंटी देता है।

मैं हर्ब सटर के गुरू द वीक श्रृंखला को पढ़ने की सलाह देता हूं , विशेष किश्तों में 59, 60 और 61।

संक्षेप में, आपके द्वारा प्रदान की जा सकने वाली अपवाद सुरक्षा के तीन स्तर हैं:

  • मूल: जब आपका कोड एक अपवाद फेंकता है, तो आपका कोड संसाधनों को लीक नहीं करता है, और ऑब्जेक्ट विनाशकारी बने रहते हैं।
  • मजबूत: जब आपका कोड अपवाद फेंकता है, तो यह एप्लिकेशन की स्थिति को अपरिवर्तित छोड़ देता है।
  • कोई फेंक नहीं: आपका कोड कभी अपवाद नहीं फेंकता।

व्यक्तिगत रूप से, मैंने इन लेखों को काफी देर से खोजा, इसलिए मेरा C ++ कोड निश्चित रूप से अपवाद-सुरक्षित नहीं है।


1
उनकी पुस्तक "असाधारण सी ++" भी एक अच्छा पढ़ा है। लेकिन मैं अभी भी EH की अवधारणा पर सवाल
उठाने

8
+1 ओपी अपवाद सुरक्षा (आमतौर पर RAII के बारे में अधिक) के साथ अपवादों को पकड़ना (उन्हें पकड़ना)
jk।

मुझे संदेह है कि बहुत कम उत्पादन सी ​​++ कोड अपवाद सुरक्षित है।
राडवल्ड

18

हम में से कुछ 20 वर्षों से अपवाद का उपयोग कर रहे हैं। उदाहरण के लिए PL / I के पास है। यह अनुमान कि वे एक नई और खतरनाक तकनीक हैं, मेरे लिए संदेहास्पद है।


कृपया मुझे गलत मत समझिए, मैं ईएच पर सवाल उठा रहा हूं (या कोशिश कर रहा हूं)। और विशेष रूप से सी ++ ईएच। और मैं विकल्प खोज रहा हूं। शायद मुझे इसे स्वीकार करना होगा (और मैं इसका एकमात्र तरीका होगा), लेकिन मुझे लगता है कि बेहतर विकल्प हो सकते हैं। यह नहीं है कि मुझे लगता है कि अवधारणा नई है, लेकिन हां, मुझे लगता है कि यह रिटर्न कोड के साथ स्पष्ट त्रुटि से अधिक खतरनाक हो सकता है ...
Frunsi

1
यदि आपको यह पसंद नहीं है, तो इसका उपयोग न करें। उन चीज़ों के आसपास प्रयास करें, जिन्हें आपको कॉल करने की आवश्यकता है, जो आपको फेंक सकते हैं, और तु-पुराने-त्रुटि-कोड को पुन: स्थापित कर सकते हैं, और उन समस्याओं से पीड़ित हो सकते हैं, जो कुछ मामलों में सहनीय हैं।
बामरगुलिस

ठीक है, एकदम सही, मैं सिर्फ ईएच और त्रुटि-कोड का उपयोग करूंगा, और इसके साथ रहूंगा। मैं एक नाइटविट हूं, मुझे अपने आप उस समाधान पर आना चाहिए था! ;)
फ्राँसी

17

सबसे पहले (जैसा कि नील ने कहा है), SEH Microsoft की संरचित अपवाद हैंडलिंग है। यह C ++ में अपवाद प्रसंस्करण के समान नहीं बल्कि समान है। वास्तव में, आपको सी ++ एक्सेप्शन हैंडलिंग को सक्षम करना होगा यदि आप इसे विजुअल स्टूडियो में चाहते हैं - डिफ़ॉल्ट व्यवहार यह गारंटी नहीं देता है कि सभी मामलों में स्थानीय ऑब्जेक्ट नष्ट हो जाते हैं! किसी भी मामले में, अपवाद हैंडलिंग वास्तव में कठिन नहीं है यह सिर्फ अलग है

अब आपके वास्तविक सवालों के लिए।

क्या आप वास्तव में अपवाद सुरक्षित कोड लिखते हैं?

हाँ। मैं सभी मामलों में अपवाद सुरक्षित कोड के लिए प्रयास करता हूं। मैं RAII तकनीकों को संसाधनों तक पहुँच के लिए उपयोग करने का प्रचार करता हूं (जैसे, boost::shared_ptrमेमोरी के लिए, boost::lock_guardलॉकिंग के लिए)। सामान्य तौर पर, RAII और स्कोप गार्डिंग तकनीकों का लगातार उपयोग अपवाद सुरक्षित कोड को लिखने में बहुत आसान बना देगा। चाल यह जानने के लिए है कि क्या मौजूद है और इसे कैसे लागू किया जाए।

क्या आप सुनिश्चित हैं कि आपका अंतिम "प्रोडक्शन रेडी" कोड अपवाद सुरक्षित है?

नहीं, यह जितना सुरक्षित है। मैं कह सकता हूं कि मैंने 24/7 गतिविधि के कई वर्षों में एक अपवाद के कारण एक प्रक्रिया दोष नहीं देखा है। मैं सही कोड की उम्मीद नहीं करता, बस अच्छी तरह से लिखा कोड। अपवाद सुरक्षा प्रदान करने के अलावा, ऊपर दी गई तकनीक एक तरह से शुद्धता की गारंटी देती है जो कि try/ catchब्लॉक के साथ प्राप्त करना असंभव है । यदि आप अपने शीर्ष नियंत्रण क्षेत्र (धागा, प्रक्रिया, आदि) में सब कुछ पकड़ रहे हैं, तो आप यह सुनिश्चित कर सकते हैं कि आप अपवादों ( ज्यादातर समय ) के सामने भागना जारी रखेंगे । एक ही तकनीक में भी मदद मिलेगी आप चलाने के लिए जारी सही ढंग से अपवाद का सामना करने में बिना try/ catchहर जगह ब्लॉक

क्या आप यह भी सुनिश्चित कर सकते हैं कि यह है?

हाँ। आप पूरी तरह से कोड ऑडिट द्वारा सुनिश्चित हो सकते हैं लेकिन कोई भी वास्तव में ऐसा नहीं करता है? नियमित कोड समीक्षा और सावधान डेवलपर्स हालांकि वहां पहुंचने के लिए एक लंबा रास्ता तय करते हैं।

क्या आप जानते हैं और / या वास्तव में काम करने वाले विकल्पों का उपयोग करते हैं?

मैंने वर्षों में कुछ बदलाव की कोशिश की है जैसे कि ऊपरी बिट्स (ala HRESULTs ) या उस भयानक setjmp() ... longjmp()हैक में एन्कोडिंग स्टेट्स । ये दोनों पूरी तरह से अलग-अलग तरीकों से व्यवहार में टूट जाते हैं।


अंत में, यदि आप कुछ तकनीकों को लागू करने की आदत में पड़ जाते हैं और ध्यान से सोचते हैं कि आप वास्तव में अपवाद के जवाब में कुछ कहां कर सकते हैं, तो आप बहुत ही पठनीय कोड के साथ समाप्त हो जाएंगे जो अपवाद सुरक्षित है। आप इन नियमों का पालन करके इसे जोड़ सकते हैं:

  • आप केवल एक विशिष्ट अपवाद के बारे में कुछ करना try/ देखना चाहते हैंcatch
  • आप लगभग कभी भी एक कच्चा newया deleteकोड नहीं देखना चाहते हैं
  • Eschew std::sprintf, snprintfऔर सामान्य रूप से सरणियों - std::ostringstreamस्वरूपण के साथ उपयोग करें और सरणियों को प्रतिस्थापित करें std::vectorऔरstd::string
  • जब संदेह हो, तो अपना रोल करने से पहले बूस्ट या एसटीएल में कार्यक्षमता देखें

मैं केवल यह सिफारिश कर सकता हूं कि आप सीखें कि अपवादों का सही उपयोग कैसे करें और परिणाम कोड के बारे में भूल जाएं यदि आप सी ++ में लिखने की योजना बनाते हैं। यदि आप अपवादों से बचना चाहते हैं, तो आप दूसरी भाषा में लिखने पर विचार कर सकते हैं जो या तो उनके पास नहीं है या उन्हें सुरक्षित बनाता है । यदि आप वास्तव में C ++ का पूरी तरह से उपयोग करना सीखना चाहते हैं, तो हर्ब सटर , निकोलाई जोसुटिस और स्कॉट मेयर्स की कुछ पुस्तकें पढ़ें ।


"डिफ़ॉल्ट व्यवहार यह गारंटी नहीं देता है कि स्थानीय वस्तुएं सभी मामलों में नष्ट हो जाती हैं" आप कह रहे हैं कि Visual Studio C ++ कंपाइलर की डिफ़ॉल्ट सेटिंग कोड का उत्पादन करती है जो अपवादों के सामने गलत है। क्या वाकई ऐसा है?
राएडवल्ड

"आप लगभग कभी भी एक कच्चा newया deleteकोड नहीं देखना चाहते हैं ": कच्चे से मुझे लगता है कि आप एक निर्माता या विध्वंसक के बाहर हैं।
राडवल्ड

@Rawalwald - re: VC ++: वीएस ++ का वीएस 200 टी संस्करण स्थानीय वस्तुओं को नष्ट नहीं करेगा, जब एक एसईएच अपवाद को फेंक दिया जाता है। "सक्षम सी ++ अपवाद हैंडलिंग" पढ़ें । VS2005 में, SEH अपवाद डिफ़ॉल्ट रूप से C ++ ऑब्जेक्ट के डिस्ट्रक्टर्स को कॉल नहीं करते हैं। यदि आप Win32 फ़ंक्शंस या सी-इंटरफ़ेस DLL में परिभाषित कुछ भी कहते हैं, तो आपको इस बारे में चिंता करनी होगी क्योंकि वे (और मौके पर) एक SEH अपवाद को अपने तरीके से फेंक देंगे।
D.Shawley

@ रैडवल्ड: री: रॉ : मूल रूप से, deleteकभी भी और इसके कार्यान्वयन के बाहर उपयोग नहीं किया जाना चाहिए tr1::shared_ptrnewप्रदान किया जा सकता है कि इसका उपयोग कुछ इस तरह है tr1::shared_ptr<X> ptr(new X(arg, arg));। महत्वपूर्ण बात यह है कि इसका परिणाम newसीधे प्रबंधित सूचक में जाता है। पर पेज boost::shared_ptrउत्तम आचरण सबसे अच्छा वर्णन करता है।
D.Shawley

अपवाद सुरक्षा के नियमों के सेट में आप std :: sprintf (et al) का उल्लेख क्यों करते हैं? ये दस्तावेज नहीं करते हैं कि वे किसी भी अपवाद को फेंक देते हैं, जैसे en.cppreference.com/w/cpp/io/c/fprintf
mabraham

10

"किसी भी लाइन को फेंक सकते हैं" इस धारणा के तहत अपवाद-सुरक्षित कोड लिखना संभव नहीं है। अपवाद-सुरक्षित कोड का डिज़ाइन कुछ विशेष अनुबंधों / गारंटीओं पर निर्भर करता है, जिन्हें आप अपने कोड में अपेक्षा, अवलोकन, अनुसरण और कार्यान्वित करने वाले हैं। कोड होना आवश्यक है जो कभी भी फेंकने की गारंटी नहीं है । वहाँ बाहर अपवाद गारंटी के अन्य प्रकार हैं।

दूसरे शब्दों में, अपवाद-सुरक्षित कोड बनाना एक बड़ी हद तक प्रोग्राम डिजाइन का विषय है, न कि केवल सादे कोडिंग का मामला ।


8
  • क्या आप वास्तव में अपवाद सुरक्षित कोड लिखते हैं?

खैर, मैं निश्चित रूप से करना चाहता हूं।

  • क्या आप सुनिश्चित हैं कि आपका अंतिम "प्रोडक्शन रेडी" कोड अपवाद सुरक्षित है?

मुझे यकीन है कि अपवादों का उपयोग करके निर्मित मेरे 24/7 सर्वर 24/7 चलते हैं और मेमोरी को लीक नहीं करते हैं।

  • क्या आप यह भी सुनिश्चित कर सकते हैं, कि यह क्या है?

यह सुनिश्चित करना बहुत मुश्किल है कि कोई भी कोड सही है। आमतौर पर, कोई केवल परिणामों से जा सकता है

  • क्या आप जानते हैं और / या वास्तव में काम करने वाले विकल्पों का उपयोग करते हैं?

नहीं, अपवाद का उपयोग करना किसी भी विकल्प की तुलना में क्लीनर और आसान है जो मैंने पिछले 30 वर्षों में प्रोग्रामिंग में उपयोग किया है।


30
इस उत्तर का कोई मूल्य नहीं है।
मैट जॉइनर

6
@MattJoiner तो सवाल का कोई मूल्य नहीं है।
मील्स राउत

5

एसईएच और सी ++ अपवादों के बीच भ्रम को छोड़कर, आपको यह जानना होगा कि अपवाद किसी भी समय फेंके जा सकते हैं, और अपने कोड को ध्यान में रखते हुए लिखें। अपवाद-सुरक्षा की आवश्यकता काफी हद तक है जो RAII, स्मार्ट पॉइंटर्स और अन्य आधुनिक C ++ तकनीकों का उपयोग करती है।

यदि आप अच्छी तरह से स्थापित पैटर्नों का पालन करते हैं, तो अपवाद-सुरक्षित कोड लिखना विशेष रूप से कठिन नहीं है, और वास्तव में यह कोड लिखने की तुलना में आसान है जो सभी मामलों में त्रुटि रिटर्न को ठीक से संभालता है।


3

ईएच अच्छा है, आम तौर पर। लेकिन C ++ का कार्यान्वयन बहुत अनुकूल नहीं है क्योंकि वास्तव में यह बताना कठिन है कि आपके अपवाद को पकड़ना कितना अच्छा है। उदाहरण के लिए जावा यह आसान बनाता है, यदि आप संभावित अपवादों को नहीं संभालते हैं तो संकलक विफल हो जाएगा।


2
यह बहुत विवेकपूर्ण उपयोग के साथ बेहतर है noexcept
22

2

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

साथ ही, IDE टूल के साथ, यह कोशिश / कैच ब्लॉक या एक अन्य कैच ब्लॉक को स्वचालित रूप से जोड़ता है।


5
जाँच की गई (जावा) और अनियंत्रित (c ++) अपवादों के बीच अंतर है (जावा में उनमें से कुछ भी हैं)। चेक किए गए अपवादों का लाभ आपने लिखा है, लेकिन इसके अपने नुकसान हैं। अंतर दृष्टिकोण और उनके पास विभिन्न समस्याओं के लिए Google।
डेविड रॉड्रिग्ज -

2

हम में से कुछ जावा जैसी भाषाओं को पसंद करते हैं जो हमें C ++ और C # की तरह अदृश्य बनाने के बजाय, तरीकों से फेंके गए सभी अपवादों को घोषित करने के लिए मजबूर करती हैं।

जब ठीक से किया जाता है, तो अपवाद त्रुटि कोड्स से बेहतर होते हैं, यदि किसी अन्य कारण से आपको कॉल श्रृंखला मैन्युअल रूप से विफलताओं का प्रचार न करना पड़े।

कहा जा रहा है, निम्न-स्तरीय API लाइब्रेरी प्रोग्रामिंग को शायद अपवाद हैंडलिंग से बचना चाहिए, और त्रुटि रिटर्न कोड से चिपके रहना चाहिए।

यह मेरा अनुभव रहा है कि C ++ में स्वच्छ अपवाद हैंडलिंग कोड लिखना मुश्किल है। मैं new(nothrow)बहुत उपयोग करके समाप्त करता हूं ।


4
और आप ज्यादातर मानक पुस्तकालय से भी बच रहे हैं? उपयोग करना new(std::nothrow)पर्याप्त नहीं है। वैसे, जावा की तुलना में C ++ में अपवाद-सुरक्षित कोड लिखना आसान है: en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
avakar

3
जावा जाँच अपवाद प्रयोज्य अत्यधिक अतिरंजित है। वास्तव में, गैर-जावा भाषाएं, उन्हें एक सफलता नहीं माना जाता है। यही कारण है कि सी ++ में "फेंक" बयान अब अप्रचलित माना जाता है, और सी # को कभी भी गंभीरता से लागू करने पर विचार नहीं किया गया (यह एक डिजाइन विकल्प था)। जावा के लिए, निम्न दस्तावेज़ ज्ञानवर्धक हो सकता है: googletesting.blogspot.com/2009/09/…
paercebal

2
यह मेरा अनुभव है कि C ++ में अपवाद-सुरक्षित कोड लिखना उतना कठिन नहीं है, और यह सामान्य रूप से क्लीनर कोड का नेतृत्व करता है। बेशक, आपको इसे करना सीखना होगा।
डेविड थॉर्नले

2

मैं अपवाद-सुरक्षित कोड लिखने के लिए अपनी पूरी कोशिश करता हूं, हां।

इसका मतलब है कि मैं ध्यान रखता हूं कि कौन सी लाइनें फेंक सकती हैं। हर कोई नहीं कर सकता है, और इसे ध्यान में रखना महत्वपूर्ण है। कुंजी वास्तव में सोचने के लिए है, और अपने कोड को संतुष्ट करने के लिए डिज़ाइन करें, मानक में परिभाषित अपवाद की गारंटी देता है।

क्या यह ऑपरेशन मजबूत अपवाद गारंटी प्रदान करने के लिए लिखा जा सकता है? क्या मुझे मूल एक के लिए समझौता करना होगा? कौन सी लाइनें अपवादों को फेंक सकती हैं, और मैं यह कैसे सुनिश्चित कर सकता हूं कि यदि वे करते हैं, तो वे वस्तु को भ्रष्ट नहीं करते हैं?


2
  • क्या आप वास्तव में अपवाद सुरक्षित कोड लिखते हैं? [ऐसा कुछ भी नहीं है। जब तक आपके पास एक प्रबंधित वातावरण नहीं है, अपवाद त्रुटि की कागज़ की ढाल हैं। यह पहले तीन प्रश्नों पर लागू होता है।]

  • क्या आप जानते हैं और / या वास्तव में काम करने वाले विकल्पों का उपयोग करते हैं? [वैकल्पिक के लिए क्या? यहां समस्या यह है कि लोग वास्तविक त्रुटियों को सामान्य प्रोग्राम ऑपरेशन से अलग नहीं करते हैं। यदि यह सामान्य प्रोग्राम ऑपरेशन है (यानी एक फ़ाइल नहीं मिली है), तो यह वास्तव में त्रुटि से निपटने में नहीं है। यदि यह एक वास्तविक त्रुटि है, तो इसे 'हैंडल' करने का कोई तरीका नहीं है या यह वास्तविक त्रुटि नहीं है। यहां आपका लक्ष्य यह पता लगाना है कि क्या गलत हुआ और या तो स्प्रेडशीट को रोकें और एक त्रुटि लॉग करें, ड्राइवर को अपने टोस्टर पर पुनरारंभ करें, या बस प्रार्थना करें कि जेटफाइटर तब भी उड़ान जारी रख सकता है जब यह सॉफ्टवेयर बगिया हो और सर्वश्रेष्ठ के लिए आशा हो।]


0

बहुत से लोग (मैं तो यहां तक ​​कहूंगा) लोग करते हैं।

अपवादों के बारे में वास्तव में महत्वपूर्ण क्या है, यदि आप किसी भी हैंडलिंग कोड को नहीं लिखते हैं - परिणाम पूरी तरह से सुरक्षित और अच्छी तरह से व्यवहार किया गया है। घबराहट के लिए बहुत उत्सुक है, लेकिन सुरक्षित है।

आपको कुछ असुरक्षित पाने के लिए संचालकों में सक्रिय रूप से गलती करने की जरूरत है, और केवल कैच (...) {} त्रुटि कोड को अनदेखा करने की तुलना करेगा।


4
सच नहीं। कोड लिखना बहुत आसान है जो अपवाद-सुरक्षित नहीं है। उदाहरण के लिए: f = new foo(); f->doSomething(); delete f;यदि doSomething विधि एक अपवाद को फेंकती है, तो आपके पास मेमोरी लीक है।
क्रिस्टोफर जॉनसन

1
इससे कोई फर्क नहीं पड़ता जब आप प्रोग्राम समाप्त करते हैं, है ना? निष्पादन जारी रखने के लिए, आपको अपवादों को सक्रिय रूप से निगलना होगा। वैसे, कुछ विशिष्ट मामले हैं, जहां बिना साफ-सफाई के समाप्ति अभी भी अस्वीकार्य है, लेकिन ऐसी स्थितियों को किसी भी प्रोग्रामिंग भाषा और शैली में विशेष देखभाल की आवश्यकता होती है।
ima

आप केवल अपवादों को अनदेखा नहीं कर सकते (और हैंडलिंग कोड नहीं लिख सकते), न तो C ++ में और न ही प्रबंधित कोड में। यह असुरक्षित होगा, और यह अच्छा व्यवहार नहीं करेगा। शायद कुछ खिलौना कोड को छोड़कर।
फ्रून्सी

1
यदि आप एप्लिकेशन कोड में अपवादों को अनदेखा करते हैं, तो बाहरी रीसोर्स शामिल होने पर प्रोग्राम अभी भी अच्छा व्यवहार नहीं कर सकता है। सच है, OS फ़ाइल के हैंडल, लॉक, सॉकेट आदि को बंद करने की परवाह करता है। लेकिन सब कुछ संभाला नहीं है, उदाहरण के लिए यह अनावश्यक फ़ाइलों या क्षति फ़ाइलों को छोड़ सकता है, जबकि उन्हें और इतने पर लिख सकते हैं। यदि आप अपवादों को अनदेखा करते हैं, तो आपको जावा में एक समस्या है, C ++ में आप RAII के साथ काम कर सकते हैं (लेकिन जब आप RAII तकनीक का उपयोग करते हैं, तो आप शायद सबसे अधिक उपयोग करते हैं क्योंकि आप अपवादों की परवाह करते हैं) ...
Frunsi

3
कृपया, मेरे शब्दों को मत तोड़ो। मैंने लिखा है "यदि आप कोई हैंडलिंग कोड नहीं लिखते हैं" - और मेरा मतलब बिल्कुल यही था। अपवादों को अनदेखा करने के लिए, आपको कोड लिखना होगा।
इमा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.