डू-ए-बचने के कुछ बेहतर तरीके क्या हैं (0); C ++ में हैक?


233

जब कोड का प्रवाह इस प्रकार हो:

if(check())
{
  ...
  ...
  if(check())
  {
    ...
    ...
    if(check())
    {
      ...
      ...
    }
  }
}

उपरोक्त गन्दे कोड प्रवाह से बचने के लिए मैंने आमतौर पर यह काम देखा है:

do {
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
} while(0);

कुछ बेहतर तरीके हैं जो इस काम के आसपास / हैक से बचते हैं ताकि यह एक उच्च-स्तरीय (उद्योग स्तर) कोड बन जाए?

किसी भी सुझाव जो बॉक्स से बाहर हैं उनका स्वागत है!


38
RAII और अपवादों को फेंक दो।
ta.speot.is

135
मेरे लिए, यह उपयोग करने के लिए एक अच्छा समय लगता है goto- लेकिन मुझे यकीन है कि कोई मुझे सुझाव देने के लिए नीचे चिह्नित करेगा, इसलिए मैं उस प्रभाव का उत्तर नहीं लिख रहा हूं। एक do ... while(0);गलत है लगता है जैसे गलत है।
मैट पीटरसन

42
@dasblinkenlight: हाँ, वास्तव में। यदि आप उपयोग करने जा रहे हैं goto, तो इसके बारे में ईमानदार breakdo ... while
रहें

44
@MatsPetersson: इसे उत्तर दें, मैं आपको आपके gotoपुनर्वास के प्रयास के लिए +1 दूंगा । :)
wilx

27
@ ta.speot.is: "RAII और थ्रो अपवाद"। ऐसा करने से, आप अपवादों के साथ प्रवाह नियंत्रण का अनुकरण करेंगे। यानी यह थोड़े महंगे, ब्लीडिंग एज हार्डवेयर को हथौड़े या पेपरवेट की तरह इस्तेमाल करने जैसा है। आप ऐसा कर सकते हैं, लेकिन यह निश्चित रूप से मेरे लिए बहुत खराब स्वाद की तरह लग रहा है।
सिग्मेट

जवाबों:


309

इन निर्णयों को किसी फ़ंक्शन में अलग करने और returnएस के बजाय एस का उपयोग करने के लिए स्वीकार्य अभ्यास माना जाता है break। जबकि ये सभी जाँच कार्य के समान स्तर के अमूर्त के अनुरूप हैं, यह काफी तार्किक दृष्टिकोण है।

उदाहरण के लिए:

void foo(...)
{
   if (!condition)
   {
      return;
   }
   ...
   if (!other condition)
   {
      return;
   }
   ...
   if (!another condition)
   {
      return;
   }
   ... 
   if (!yet another condition)
   {
      return;
   }
   ...
   // Some unconditional stuff       
}

22
@MatsPetersson: "फ़ंक्शन में पृथक" का अर्थ है एक नए फ़ंक्शन में रिफैक्टिंग करना जो केवल परीक्षण करता है।
MSalters

35
+1। यह भी एक अच्छा जवाब है। C ++ 11 में, अलग-थलग कार्य एक लंबोदर हो सकता है, क्योंकि यह स्थानीय चर को भी पकड़ सकता है, इसलिए चीजें आसान हो जाती हैं!
नवाज

4
@deworde: स्थिति के आधार पर यह समाधान गोटो की तुलना में अधिक लंबा और कम पठनीय हो सकता है। चूँकि C ++ दुर्भाग्यवश, स्थानीय फंक्शन डेमिनेशन को अनुमति नहीं देता है, तो आपको उस फंक्शन को स्थानांतरित करना होगा (जिस पर आप लौटने वाले हैं) कहीं और, जो पठनीयता को कम कर देता है। यह दर्जनों मापदंडों के साथ समाप्त हो सकता है, फिर, क्योंकि दर्जनों पैरामीटर एक खराब चीज हैं, आप उन्हें संरचना में लपेटने का निर्णय लेंगे, जो नए डेटा प्रकार का निर्माण करेगा। एक साधारण स्थिति के लिए बहुत अधिक टाइपिंग।
सिग्मेट

24
@ डैमोन अगर कुछ भी है, returnतो क्लीनर है क्योंकि किसी भी पाठक को तुरंत पता चल जाता है कि यह सही ढंग से काम करता है और यह क्या करता है। इसके साथ gotoआपको यह देखने के लिए चारों ओर देखना होगा कि यह क्या है, और यह सुनिश्चित करने के लिए कि कोई गलती नहीं हुई। भटकाव का लाभ है।
आर। मार्टिनो फर्नांडीस

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

257

ऐसे समय होते हैं जब gotoवास्तव में सही उत्तर का उपयोग किया जाता है - कम से कम उन लोगों के लिए जिन्हें धार्मिक विश्वास में नहीं लाया जाता है "goto कभी भी उत्तर नहीं हो सकता है, चाहे कोई भी प्रश्न हो" - और यह उन मामलों में से एक है।

यह कोड do { ... } while(0);एक के gotoरूप में ड्रेसिंग के एकमात्र उद्देश्य के लिए हैक का उपयोग कर रहा है break। यदि आप उपयोग करने जा रहे हैंgoto , तो इसके बारे में खुले रहें। पढ़ने के लिए कोड HARDER बनाने का कोई मतलब नहीं है।

एक विशेष स्थिति बस तब होती है जब आपके पास काफी जटिल परिस्थितियों के साथ बहुत सारे कोड होते हैं:

void func()
{
   setup of lots of stuff
   ...
   if (condition)
   {
      ... 
      ...
      if (!other condition)
      {
          ...
          if (another condition)
          {
              ... 
              if (yet another condition)
              {
                  ...
                  if (...)
                     ... 
              }
          }
      }
  .... 

  }
  finish up. 
}

यह वास्तव में यह स्पष्ट कर सकता है कि इस तरह के एक जटिल तर्क के न होने से कोड सही है।

void func()
{
   setup of lots of stuff
   ...
   if (!condition)
   {
      goto finish;
   }
   ... 
   ...
   if (other condition)
   {
      goto finish;
   }
   ...
   if (!another condition)
   {
      goto finish;
   }
   ... 
   if (!yet another condition)
   {
      goto finish;
   }
   ... 
   .... 
   if (...)
         ...    // No need to use goto here. 
 finish:
   finish up. 
}

संपादित करें: स्पष्ट करने के लिए, मैं किसी भी तरह से gotoएक सामान्य समाधान के उपयोग का प्रस्ताव नहीं कर रहा हूं । लेकिन ऐसे मामले हैं जहांgoto अन्य समाधानों की तुलना में बेहतर समाधान है।

उदाहरण के लिए कल्पना करें कि हम कुछ डेटा एकत्र कर रहे हैं, और जिन परिस्थितियों के लिए परीक्षण किया जा रहा है, वे कुछ इस प्रकार हैं "यह डेटा एकत्र होने का अंत है" - जो किसी प्रकार के "जारी / अंत" मार्करों पर निर्भर करता है जो कि जहां पर निर्भर करता है आप डेटा स्ट्रीम में हैं।

अब, जब हम काम कर रहे होते हैं, हमें डेटा को फ़ाइल में सहेजने की आवश्यकता होती है।

और हां, अक्सर अन्य समाधान होते हैं जो एक उचित समाधान प्रदान कर सकते हैं, लेकिन हमेशा नहीं।


76
सहमत नहीं हैं। gotoजगह हो सकती है, लेकिन goto cleanupनहीं। सफाई RAII के साथ की जाती है।
MSalters

14
@MSalters: यह मानता है कि सफाई में कुछ ऐसा शामिल है जिसे RAII के साथ हल किया जा सकता है। शायद मुझे "इश्यू एरर" या इसके बजाय कुछ ऐसा कहना चाहिए था।
मैट पीटरसन

25
इसलिए, इस पर विश्वास करते रहें, संभवतः उन धार्मिक विश्वासों से gotoजो सही उत्तर नहीं है। मैं सराहना करता हूँ अगर वहाँ एक टिप्पणी थी ...
मैट पीटरसन

19
लोग नफरत करते हैं gotoक्योंकि आपको इसका उपयोग करने के लिए सोचना पड़ता है / एक प्रोग्राम को समझने के लिए जो इसका उपयोग करता है ... माइक्रोप्रोसेसर, दूसरी ओर, निर्मित होते हैं jumpsऔर conditional jumps... इसलिए समस्या कुछ लोगों के साथ होती है, तर्क या कुछ और के साथ नहीं।
woliveirajr

17
के उचित उपयोग के लिए +1 goto। आरए II है नहीं के रूप में है कि अपवाद का दुरुपयोग हो सकता है, सही समाधान अगर त्रुटियों उम्मीद कर रहे हैं (असाधारण नहीं)।
जो

82

आप एक boolचर के साथ एक सरल निरंतरता पैटर्न का उपयोग कर सकते हैं :

bool goOn;
if ((goOn = check0())) {
    ...
}
if (goOn && (goOn = check1())) {
    ...
}
if (goOn && (goOn = check2())) {
    ...
}
if (goOn && (goOn = check3())) {
    ...
}

निष्पादन की यह श्रृंखला के रूप में जैसे ही बंद हो जाएगा checkNरिटर्न एक false। ऑपरेटर check...()की शॉर्ट-सर्किटिंग के कारण आगे कोई कॉल नहीं किया जाएगा &&। इसके अलावा, के अनुकूलन compilers कि सेटिंग पहचान करने के लिए स्मार्ट पर्याप्त हैं goOnकरने के लिए falseएक एक रास्ता है, और याद आ रही डालने goto endके लिए। परिणामस्वरूप, ऊपर दिए गए कोड का प्रदर्शन ए do/ के समान होगा while(0), केवल इसकी पठनीयता के लिए एक दर्दनाक झटका के बिना।


30
ifशर्तों के अंदर असाइनमेंट बहुत संदिग्ध लगते हैं ।
मिखाइल

90
@ मिखाइल केवल एक अप्रशिक्षित आंख के लिए।
dasblinkenlight

20
मैंने पहले भी इस तकनीक का उपयोग किया है और यह मुझे हमेशा इस तरह से गुदगुदी करता है कि मुझे लगा कि कंपाइलर को हर एक ifमामले की जाँच के लिए कोड जनरेट करना होगा, goOnभले ही वह कितना भी जल्दी फेल हो गया हो (जैसा कि कूदने / तोड़ने का विरोध हो) ... लेकिन मैंने अभी-अभी एक परीक्षण चलाया और VS2012 कम से कम पहले से ही झूठे होने के बाद सब कुछ शॉर्ट-सर्किट करने के लिए पर्याप्त स्मार्ट था। मैं इसे अधिक बार उपयोग करूंगा। नोट: यदि आप उपयोग करते हैं goOn &= checkN()तो checkN()हमेशा चलेगा भले ही goOnवह falseशुरुआत में था if(यानी, ऐसा न करें)।
मार्क

11
@ नवाज़: अगर आपके पास एक अप्रशिक्षित दिमाग है, जो कोड आधार पर मनमाने ढंग से बदलाव कर रहा है, तो आपके पास ifएस के अंदर असाइनमेंट्स की तुलना में बहुत बड़ी समस्या है ।
इडेलिक

6
@ शिशिर लालित्य देखने वाले की नज़र में ऐसा है! मैं यह समझने में असफल रहा कि दुनिया में लूपिंग निर्माण का दुरुपयोग किसी तरह से "सुरुचिपूर्ण" माना जा सकता है, लेकिन शायद यह सिर्फ मेरे लिए है।
dasblinkenlight

38
  1. कोड को एक अलग फ़ंक्शन (या शायद एक से अधिक) में निकालने का प्रयास करें। चेक फेल होने पर फंक्शन से वापस लौटें।

  2. यदि ऐसा करने के लिए आसपास के कोड के साथ बहुत कसकर युग्मित किया गया है, और आपको युग्मन को कम करने का कोई तरीका नहीं मिल रहा है, तो इस ब्लॉक के बाद कोड को देखें। संभवतः, यह फ़ंक्शन द्वारा उपयोग किए जाने वाले कुछ संसाधनों को साफ करता है। RAII ऑब्जेक्ट का उपयोग करके इन संसाधनों को प्रबंधित करने का प्रयास करें ; फिर प्रत्येक डोडी breakको बदलें return(या throw, यदि वह अधिक उपयुक्त है) और ऑब्जेक्ट के विध्वंसक को आपके लिए साफ कर दें।

  3. यदि प्रोग्राम का प्रवाह (आवश्यक रूप से) इतना स्क्वीगली है कि आपको वास्तव में एक की आवश्यकता है goto, तो इसे एक अजीब भेष देने के बजाय इसका उपयोग करें।

  4. यदि आपके पास कोडिंग नियम हैं जो नेत्रहीन रूप से मना करते हैं goto, और आप वास्तव में कार्यक्रम के प्रवाह को सरल नहीं कर सकते हैं, तो आपको शायद इसे अपने doहैक के साथ भेस देना होगा ।


4
मैं विनम्रतापूर्वक उस आरएआई को प्रस्तुत करता हूं, जबकि उपयोगी, चांदी की गोली नहीं है। जब आप अपने आप को एक कन्वर्ट-गोटो-टू-आरएआई वर्ग लिखने के बारे में पाते हैं, जिसका कोई अन्य उपयोग नहीं है, तो मुझे लगता है कि आपको "गोटो-एंड-ऑफ-द-वर्ल्ड" मुहावरों का उपयोग करना बेहतर होगा।
मूर्खतापूर्ण

1
@busy_wait: वास्तव में, RAII सब कुछ हल नहीं कर सकता; यही कारण है कि मेरा जवाब दूसरे बिंदु के साथ नहीं रुकता है, लेकिन यह सुझाव देने के लिए जाता है कि gotoक्या यह वास्तव में एक बेहतर विकल्प है।
माइक सेमोर

3
मैं सहमत हूं, लेकिन मुझे लगता है कि गोटो-आरएआई रूपांतरण कक्षाएं लिखना एक बुरा विचार है और मुझे लगता है कि इसे स्पष्ट रूप से कहा जाना चाहिए।
आलसी

@idoby एक RAII टेम्प्लेट क्लास लिखने के बारे में क्या है , और एक लैम्ब्डा में जो भी सिंगल
यूज़

@ कैलेथ मुझे लगता है जैसे आप ctors / dtors को मजबूत कर रहे हैं?
मूढ़

37

TLDR : RAII , लेन-देन कोड (केवल पहले से ही गणना होने पर परिणाम और रिटर्न सामान सेट) और अपवाद।

लंबा जवाब:

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

error_code_type c_to_refactor(result_type *r)
{
    error_code_type result = error_ok; //error_code_type/error_ok defd. elsewhere
    some_resource r1, r2; // , ...;
    if(error_ok != (result = computation1(&r1))) // Allocates local resources
        goto cleanup;
    if(error_ok != (result = computation2(&r2))) // Allocates local resources
        goto cleanup;
    // ...

    // Commit code: all operations succeeded
    *r = computed_value_n;
cleanup:
    free_resource1(r1);
    free_resource2(r2);
    return result;
}

सी में, सबसे codebases में, if(error_ok != ...और gotoकोड आम तौर पर कुछ सुविधा मैक्रो के पीछे छिपा है ( RET(computation_result),ENSURE_SUCCESS(computation_result, return_code) , आदि) के ।

C ++ C पर अतिरिक्त टूल प्रदान करता है :

  • सफाई ब्लॉक कार्यक्षमता को आरएआईआई के रूप में लागू किया जा सकता है, जिसका अर्थ है कि आपको अब पूरे cleanupब्लॉक की आवश्यकता नहीं है और शुरुआती रिटर्न स्टेटमेंट को जोड़ने के लिए क्लाइंट कोड को सक्षम करना होगा।

  • जब भी आप जारी नहीं रख सकते हैं, तो आप if(error_ok != ...सीधे-सीधे कॉल में बदल सकते हैं।

समतुल्य सी ++ कोड:

result_type cpp_code()
{
    raii_resource1 r1 = computation1();
    raii_resource2 r2 = computation2();
    // ...
    return computed_value_n;
}

यह सबसे अच्छा अभ्यास है क्योंकि:

  • यह स्पष्ट है (यह है, जबकि त्रुटि से निपटने स्पष्ट नहीं है, एल्गोरिथ्म का मुख्य प्रवाह है)

  • क्लाइंट कोड लिखना सीधा है

  • यह न्यूनतम है

  • यह आसान है

  • इसका कोई दोहराव कोड नहीं है

  • यह कोई मैक्रो का उपयोग करता है

  • यह अजीब do { ... } while(0)निर्माण का उपयोग नहीं करता है

  • यह न्यूनतम प्रयास के साथ पुन: प्रयोज्य है (यदि मैं कॉल को computation2();एक अलग फ़ंक्शन में कॉपी करना चाहता हूं, तो मुझे यह सुनिश्चित करने की आवश्यकता नहीं है कि मैं do { ... } while(0)नए कोड में एक जोड़ #defineदूं , न ही एक गोटो रैपर मैक्रो और एक क्लीनअप लेबल, न ही और कुछ)।


+1। यह मैं RAII या कुछ और का उपयोग करके उपयोग करने की कोशिश करता हूं। shared_ptrएक कस्टम डिलेटर के साथ बहुत सारे सामान कर सकते हैं। C ++ 11 में लैम्ब्डा के साथ और भी आसान।
मैके

यह सच है, लेकिन अगर आप उन चीजों के लिए साझा_पार्ट का उपयोग करते हैं जो पॉइंटर्स नहीं हैं, तो कम से कम टाइप-बीफ़-आईएनजी पर विचार करें: namespace xyz { typedef shared_ptr<some_handle> shared_handle; shared_handle make_shared_handle(a, b, c); };इस मामले में ( make_handleनिर्माण पर सही डेलेटर प्रकार सेट करने के साथ ), प्रकार का नाम अब यह इंगित करता है कि यह एक पॉइंटर है ।
utnapistim

21

मैं पूर्णता के लिए एक उत्तर जोड़ रहा हूं। कई अन्य उत्तरों ने बताया कि बड़ी स्थिति ब्लॉक को एक अलग फ़ंक्शन में विभाजित किया जा सकता है। लेकिन जैसा कि कई बार बताया गया कि यह दृष्टिकोण मूल संदर्भ से सशर्त कोड को अलग करता है। यह एक कारण है कि लैम्बदास को भाषा में C ++ 11 में जोड़ा गया था। लैंबडास का उपयोग दूसरों द्वारा सुझाया गया था लेकिन कोई स्पष्ट नमूना प्रदान नहीं किया गया था। मैंने इस उत्तर में एक डाल दिया है। मुझे क्या आघात पहुँचता है, यह do { } while(0)कई तरीकों से दृष्टिकोण के समान लगता है - और शायद इसका मतलब यह है कि यह अभी भी एक gotoभेस में है ...।

earlier operations
...
[&]()->void {

    if (!check()) return;
    ...
    ...
    if (!check()) return;
    ...
    ...
    if (!check()) return;
    ...
    ...
}();
later operations

7
मेरे लिए यह हैक की तुलना में बुरा है ... जबकि हैक।
माइकल

18

निश्चित रूप से उत्तर नहीं , बल्कि उत्तर है (पूर्णता के लिए)

के बजाय :

do {
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
} while(0);

आप लिख सकते हैं:

switch (0) {
case 0:
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
}

यह अभी भी भेस में एक गोटो है, लेकिन कम से कम यह किसी भी अधिक लूप नहीं है। जिसका अर्थ है कि आपको बहुत सावधानी से जांचना होगा कि कुछ जारी नहीं है ब्लॉक में छिपा कहीं।

निर्माण भी सरल है कि आप उम्मीद कर सकते हैं कि कंपाइलर इसे दूर का अनुकूलन करेगा।

जैसा कि @jamesdlin द्वारा सुझाया गया है, आप यहां तक ​​कि जैसे मैक्रो के पीछे छिपा सकते हैं

#define BLOC switch(0) case 0:

और इसका उपयोग करें

BLOC {
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
}

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


2
ऊह, चतुर। तुम भी इसे एक मैक्रो की तरह छुपा सकते हैं define BLOCK switch (0) case 0:और इसका उपयोग कर सकते हैं BLOCK { ... break; }
jamesdlin

@jamesdlin: यह मेरे लिए पहले कभी नहीं हुआ है कि यह उपयोगी हो सकता है एक स्विच के खुलने वाले ब्रैकेट से पहले एक मामला डाल दिया। लेकिन यह वास्तव में सी द्वारा अनुमत है और इस मामले में एक अच्छा मैक्रो लिखना आसान है।
क्रिस् स।

15

मैं मैट के समान एक दृष्टिकोण की सिफारिश करूंगा जो कि अनावश्यक का जवाब देता है goto। केवल फ़ंक्शन में सशर्त तर्क रखें। कॉलर में फ़ंक्शन को लागू करने से पहले या बाद में हमेशा चलने वाला कोई भी कोड:

void main()
{
    //do stuff always
    func();
    //do other stuff always
}

void func()
{
    if (!condition)
        return;
    ...
    if (!other condition)
        return;
    ...
    if (!another condition)
        return;
    ... 
    if (!yet another condition)
        return;
    ...
}

3
यदि आपको बीच में एक और संसाधन हासिल करना है func, तो आपको एक अन्य फ़ंक्शन (अपने पैटर्न के अनुसार) को फैक्टर-आउट करना होगा। यदि इन सभी अलग-अलग कार्यों के लिए समान डेटा की आवश्यकता होती है, तो आप बस एक ही स्टैक तर्कों को बार-बार कॉपी करेंगे, या ढेर पर अपनी दलीलें आवंटित करने और एक पॉइंटर पास करने का फैसला करेंगे, न कि भाषा की विशेषता के सबसे मूल का लाभ उठाते हुए; समारोह तर्क)। संक्षेप में, मुझे विश्वास नहीं है कि यह घोल सबसे खराब स्थिति के लिए है जहाँ हर हालत की जाँच नए संसाधनों को प्राप्त करने के बाद की जाती है जिन्हें साफ़ किया जाना चाहिए। नोट नवाब के बारे में टिप्पणी हालांकि लांबडा।
tn

2
मुझे ओपी को अपने कोड ब्लॉक के बीच में संसाधन प्राप्त करने के बारे में कुछ भी कहना याद नहीं है, लेकिन मैं इसे एक व्यावहारिक आवश्यकता के रूप में स्वीकार करता हूं। उस स्थिति में स्टैक पर संसाधन को घोषित करने func()और इसके विध्वंसक को मुक्त संसाधनों को संभालने की अनुमति देने में क्या गलत है ? यदि बाहर के किसी भी चीज func()को उसी संसाधन तक पहुंच की आवश्यकता है तो उसे func()उचित संसाधन प्रबंधक द्वारा कॉल करने से पहले ढेर पर घोषित किया जाना चाहिए ।
डैन बेहार्ड 29'13

12

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


11

यदि आपको निष्पादन के दौरान स्थानीय चर पेश करने की आवश्यकता नहीं है, तो आप अक्सर इसे समतल कर सकते हैं:

if (check()) {
  doStuff();
}  
if (stillOk()) {
  doMoreStuff();
}
if (amIStillReallyOk()) {
  doEvenMore();
}

// edit 
doThingsAtEndAndReportErrorStatus()

2
लेकिन तब प्रत्येक स्थिति में पिछले एक को शामिल करना होगा, जो न केवल बदसूरत है, बल्कि प्रदर्शन के लिए संभावित रूप से खराब है। जैसे ही हम जानते हैं कि हम "ठीक नहीं" हैं, उन चेक और सफाई पर कूदना बेहतर है।
tn

यह सच है, हालांकि इस दृष्टिकोण के फायदे हैं यदि कोई ऐसी चीजें हैं जो अंत में की जानी हैं, तो आप जल्दी वापस नहीं आना चाहते (देखें संपादित करें)। यदि आप परिस्थितियों में बूल का उपयोग करने के डेनिस के दृष्टिकोण के साथ जोड़ते हैं, तो प्रदर्शन हिट नगण्य होगा जब तक कि यह बहुत तंग लूप में न हो।
the_mandrill

10

Dasblinkenlight के उत्तर के समान है, लेकिन ifएक कोड समीक्षक द्वारा "नियत" प्राप्त करने के लिए इसके अंदर असाइनमेंट से बचा जाता है :

bool goOn = check0();
if (goOn) {
    ...
    goOn = check1();
}
if (goOn) {
    ...
    goOn = check2();
}
if (goOn) {
    ...
}

...

मैं इस पैटर्न का उपयोग करता हूं जब अगले चरण से पहले एक कदम के परिणामों की जांच की जानी चाहिए, जो एक ऐसी स्थिति से भिन्न होता है जहां सभी प्रकार के चेक एक बड़े if( check1() && check2()...प्रकार के पैटर्न के साथ सामने किए जा सकते हैं ।


ध्यान दें कि check1 () वास्तव में PerformStep1 () हो सकता है जो चरण के परिणाम कोड देता है। यह आपके प्रक्रिया प्रवाह समारोह की जटिलता को कम रखेगा।
डेनिस स्किडमोर

10

अपवादों का उपयोग करें। आपका कोड बहुत अधिक क्लीनर दिखेगा (और अपवाद एक कार्यक्रम के निष्पादन प्रवाह में त्रुटियों से निपटने के लिए बिल्कुल बनाया गया था)। संसाधनों की सफाई के लिए (फाइल डिस्क्रिप्टर, डेटाबेस कनेक्शन, आदि), लेख पढ़ें C ++ एक "अंत" निर्माण क्यों नहीं प्रदान करता है?

#include <iostream>
#include <stdexcept>   // For exception, runtime_error, out_of_range

int main () {
    try {
        if (!condition)
            throw std::runtime_error("nope.");
        ...
        if (!other condition)
            throw std::runtime_error("nope again.");
        ...
        if (!another condition)
            throw std::runtime_error("told you.");
        ...
        if (!yet another condition)
            throw std::runtime_error("OK, just forget it...");
    }
    catch (std::runtime_error &e) {
        std::cout << e.what() << std::endl;
    }
    catch (...) {
        std::cout << "Caught an unknown exception\n";
    }
    return 0;
}

10
वास्तव में? सबसे पहले, मैं ईमानदारी से वहाँ कोई पठनीयता सुधार नहीं देख रहा हूँ। कार्यक्रम प्रवाह को नियंत्रित करने के लिए उपयोग की आवश्यकता नहीं है। यह उनका उचित उद्देश्य नहीं है। इसके अलावा, अपवाद महत्वपूर्ण प्रदर्शन दंड लाते हैं। अपवाद के लिए उचित उपयोग का मामला तब है जब कुछ स्थिति मौजूद है कि आप इस बारे में कुछ भी नहीं कर सकते हैं, जैसे कि एक ऐसी फ़ाइल को खोलने की कोशिश करना जो पहले से ही किसी अन्य प्रक्रिया द्वारा बंद है, या नेटवर्क कनेक्शन विफल रहता है, या डेटाबेस में कॉल विफल हो जाता है। , या एक कॉलर एक प्रक्रिया के लिए एक अमान्य पैरामीटर गुजरता है। इस तरह की चीज। लेकिन कार्यक्रम के प्रवाह को नियंत्रित करने के लिए अपवादों का उपयोग न करें।
क्रेग

3
मेरा मतलब है, इसके बारे में नहीं बताया जा सकता है, लेकिन उस Stroustrup लेख पर जाएं जिसे आपने संदर्भित किया है और "मुझे क्या करना चाहिए इसके लिए अपवादों का उपयोग नहीं करना चाहिए?" अनुभाग। अन्य बातों के अलावा, वे कहते हैं: "विशेष रूप से, थ्रो केवल एक फ़ंक्शन से मान लौटाने का एक वैकल्पिक तरीका नहीं है (वापसी के समान)। ऐसा करना धीमा होगा और अधिकांश सी ++ प्रोग्रामर को भ्रमित करेगा जो केवल त्रुटि के लिए उपयोग किए गए अपवादों को जब्त करने के लिए उपयोग किया जाता है। हैंडलिंग। इसी तरह, फेंक एक पाश से बाहर निकलने का एक अच्छा तरीका नहीं है। "
क्रेग

3
@ क्रेग जो आपने बताया है वह सही है, लेकिन आप यह मान रहे हैं कि नमूना कार्यक्रम एक check()शर्त के विफल होने के बाद भी जारी रह सकता है , और यह प्रमाणित रूप से आपकी धारणा है, नमूने में कोई संदर्भ नहीं है। यह मानते हुए कि कार्यक्रम जारी नहीं रह सकता है, अपवादों का उपयोग करने का तरीका है।
कार्टूको

2
ठीक है, अगर यह वास्तव में संदर्भ है तो पर्याप्त है। लेकिन, मान्यताओं के बारे में मजेदार बात ... ;-)
क्रेग

10

मेरे do{...}while(0)लिए ठीक है। यदि आप इसे नहीं देखना चाहते हैं do{...}while(0), तो आप उनके लिए वैकल्पिक कीवर्ड परिभाषित कर सकते हैं।

उदाहरण:

//--------SomeUtilities.hpp---------
#define BEGIN_TEST do{
#define END_TEST }while(0);

//--------SomeSourceFile.cpp--------
BEGIN_TEST
   if(!condition1) break;
   if(!condition2) break;
   if(!condition3) break;
   if(!condition4) break;
   if(!condition5) break;

   //processing code here

END_TEST

मुझे लगता है कि संकलक अनावश्यक while(0)स्थिति को दूर कर देगाdo{...}while(0) द्विआधारी संस्करण में और ब्रेक को बिना शर्त कूद में बदल देगा। आप यह सुनिश्चित करने के लिए विधानसभा भाषा संस्करण की जाँच कर सकते हैं।

उपयोग करने से gotoक्लीनर कोड भी बनता है और यह स्थिति के साथ सीधा-सीधा है। आप निम्न कार्य कर सकते हैं:

{
   if(!condition1) goto end_blahblah;
   if(!condition2) goto end_blahblah;
   if(!condition3) goto end_blahblah;
   if(!condition4) goto end_blahblah;
   if(!condition5) goto end_blahblah;

   //processing code here

 }end_blah_blah:;  //use appropriate label here to describe...
                   //  ...the whole code inside the block.

नोट बंद होने के बाद लेबल लगाया जाता है }। यह एक संभावित समस्या है gotoजिससे आप गलती से बीच में एक कोड रख रहे हैं क्योंकि आपने लेबल नहीं देखा है। यह अब do{...}while(0)बिना कंडीशन कोड जैसा है ।

इस कोड को क्लीनर और अधिक समझदार बनाने के लिए, आप यह कर सकते हैं:

//--------SomeUtilities.hpp---------
#define BEGIN_TEST {
#define END_TEST(_test_label_) }_test_label_:;
#define FAILED(_test_label_) goto _test_label_

//--------SomeSourceFile.cpp--------
BEGIN_TEST
   if(!condition1) FAILED(NormalizeData);
   if(!condition2) FAILED(NormalizeData);
   if(!condition3) FAILED(NormalizeData);
   if(!condition4) FAILED(NormalizeData);
   if(!condition5) FAILED(NormalizeData);

END_TEST(NormalizeData)

इसके साथ, आप नेस्टेड ब्लॉक कर सकते हैं और निर्दिष्ट कर सकते हैं कि आप कहाँ से बाहर निकलना / कूदना चाहते हैं।

//--------SomeUtilities.hpp---------
#define BEGIN_TEST {
#define END_TEST(_test_label_) }_test_label_:;
#define FAILED(_test_label_) goto _test_label_

//--------SomeSourceFile.cpp--------
BEGIN_TEST
   if(!condition1) FAILED(NormalizeData);
   if(!condition2) FAILED(NormalizeData);

   BEGIN_TEST
      if(!conditionAA) FAILED(DecryptBlah);
      if(!conditionBB) FAILED(NormalizeData);   //Jump out to the outmost block
      if(!conditionCC) FAILED(DecryptBlah);

      // --We can now decrypt and do other stuffs.

   END_TEST(DecryptBlah)

   if(!condition3) FAILED(NormalizeData);
   if(!condition4) FAILED(NormalizeData);

   // --other code here

   BEGIN_TEST
      if(!conditionA) FAILED(TrimSpaces);
      if(!conditionB) FAILED(TrimSpaces);
      if(!conditionC) FAILED(NormalizeData);   //Jump out to the outmost block
      if(!conditionD) FAILED(TrimSpaces);

      // --We can now trim completely or do other stuffs.

   END_TEST(TrimSpaces)

   // --Other code here...

   if(!condition5) FAILED(NormalizeData);

   //Ok, we got here. We can now process what we need to process.

END_TEST(NormalizeData)

स्पेगेटी कोड की गलती नहीं है goto, यह प्रोग्रामर की गलती है। आप अभी भी उपयोग किए बिना स्पेगेटी कोड का उत्पादन कर सकते हैं goto


10
मैं gotoएक लाख बार प्रीप्रोसेसर का उपयोग करके भाषा सिंटैक्स का विस्तार करना चाहूंगा ।
ईसाई

2
"इस कोड को क्लीनर और अधिक समझदार बनाने के लिए, आप [LOADS_OF_WEIRD_MACROS] का उपयोग कर सकते हैं : " गणना नहीं करता है।
अंडरस्कोर_ड

8

यह एक कार्यात्मक प्रोग्रामिंग दृष्टिकोण से एक अच्छी तरह से ज्ञात और अच्छी तरह से हल की गई समस्या है - शायद मोनाड।

नीचे दी गई टिप्पणी के जवाब में मैंने अपना परिचय यहां संपादित किया है: आप विभिन्न स्थानों में सी ++ मोनाड्स को लागू करने के बारे में पूरी जानकारी पा सकते हैं जो आपको यह बताने की अनुमति देगा कि रॉटसर क्या सुझाव देता है। भिक्षुओं को कुछ समय लगता है इसलिए इसके बजाय मैं यहां एक त्वरित "गरीब-आदमी" मोनड-जैसे तंत्र का सुझाव देने जा रहा हूं, जिसके लिए आपको बढ़ावा देने के अलावा और कुछ नहीं जानना चाहिए: वैकल्पिक।

अपने गणना चरणों को निम्नानुसार सेट करें:

boost::optional<EnabledContext> enabled(boost::optional<Context> context);
boost::optional<EnergisedContext> energised(boost::optional<EnabledContext> context);

प्रत्येक कम्प्यूटेशनल कदम स्पष्ट रूप से रिटर्न की तरह कुछ कर सकता है boost::noneयदि वैकल्पिक यह दिया गया था तो खाली है। उदाहरण के लिए:

struct Context { std::string coordinates_filename; /* ... */ };

struct EnabledContext { int x; int y; int z; /* ... */ };

boost::optional<EnabledContext> enabled(boost::optional<Context> c) {
   if (!c) return boost::none; // this line becomes implicit if going the whole hog with monads
   if (!exists((*c).coordinates_filename)) return boost::none; // return none when any error is encountered.
   EnabledContext ec;
   std::ifstream file_in((*c).coordinates_filename.c_str());
   file_in >> ec.x >> ec.y >> ec.z;
   return boost::optional<EnabledContext>(ec); // All ok. Return non-empty value.
}

फिर उन्हें एक साथ जंजीर:

Context context("planet_surface.txt", ...); // Close over all needed bits and pieces

boost::optional<EnergisedContext> result(energised(enabled(context)));
if (result) { // A single level "if" statement
    // do work on *result
} else {
    // error
}

इसके बारे में अच्छी बात यह है कि आप प्रत्येक कम्प्यूटेशनल चरण के लिए स्पष्ट रूप से परिभाषित इकाई परीक्षण लिख सकते हैं। इसके अलावा आह्वान सादा अंग्रेजी की तरह पढ़ता है (जैसा कि आमतौर पर कार्यात्मक शैली के साथ होता है)।

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


3
इस कोड में पिछले फ़ंक्शन की विफलता को संभालने के लिए प्रत्येक व्यक्तिगत फ़ंक्शन को मजबूर करने की एक अवांछित संपत्ति है, इस प्रकार मोनाड मुहावरे का उपयोग ठीक से नहीं किया जाता है (जहां मोनडिक प्रभाव, इस मामले में विफलता को स्पष्ट रूप से नियंत्रित किया जाना चाहिए)। ऐसा करने के लिए आपको optional<EnabledContext> enabled(Context); optional<EnergisedContext> energised(EnabledContext);फ़ंक्शन एप्लिकेशन के बजाय इसके बजाय मोनैडिक कंपोज़िशन ऑपरेशन ('बाइंड') का उपयोग करना होगा।
Rotsor

धन्यवाद। आप सही हैं - इसे ठीक से करने का तरीका है। मैं अपने उत्तर में यह बताने के लिए बहुत अधिक नहीं लिखना चाहता था कि (इसलिए "गरीब-आदमी" शब्द का मतलब यह था कि मैं यह सुझाव देने के लिए था कि मैं यहाँ पूरी तरह से नहीं जा रहा हूँ)।
बेनेडिक्ट

7

एक संख्यात्मक या आयतन परिणाम देने वाले अतिरिक्त फ़ंक्शन में बयानों को स्थानांतरित करने के बारे में कैसे?

int ConditionCode (void) {
   if (condition1)
      return 1;
   if (condition2)
      return 2;
   ...
   return 0;
}


void MyFunc (void) {
   switch (ConditionCode ()) {
      case 1:
         ...
         break;

      case 2:
         ...
         break;

      ...

      default:
         ...
         break;
   }
}

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

यहाँ समस्या आप अलग कारण और परिणाम है। यानी आप एक ही कोड को अलग करते हैं जो समान स्थिति संख्या को संदर्भित करता है और यह आगे के बग का स्रोत हो सकता है।
रीगा

@kriss: ठीक है, इस पर ध्यान देने के लिए कंडीशनकोड () फ़ंक्शन को समायोजित किया जा सकता है। उस फ़ंक्शन का मुख्य बिंदु यह है कि आप एक अंतिम स्थिति की गणना करते ही स्वच्छ निकास के लिए रिटर्न <परिणाम> का उपयोग कर सकते हैं; और यही वह है जो यहां संरचनात्मक स्‍पष्‍टता प्रदान कर रहा है।
karx11erx

@ रीगा: इमो ये पूरी तरह से अकादमिक आपत्तियां हैं। मैं C ++ को हर नए संस्करण के साथ अधिक जटिल, गूढ़ और अपठनीय बनाता हूं। मैं छोटे हेल्पर फ़ंक्शन के साथ एक समस्या नहीं देख सकता हूं, जो लक्ष्य को अधिक पठनीय के ठीक नीचे बनाने के लिए एक अच्छी तरह से संरचित तरीके से जटिल परिस्थितियों का मूल्यांकन करता है।
karx11erx

1
@ karx11erx मेरी आपत्तियां व्यावहारिक हैं और मेरे अनुभव पर आधारित हैं। यह पैटर्न C ++ 11 या जो भी भाषा है, उससे असंबंधित है। यदि आपको भाषा निर्माण में कठिनाइयाँ आती हैं जो आपको अच्छी वास्तुकला लिखने की अनुमति देती हैं जो भाषा की समस्या नहीं है।
रीगा

5

शायद ऐसा ही कुछ

#define EVER ;;

for(EVER)
{
    if(!check()) break;
}

या अपवाद का उपयोग करें

try
{
    for(;;)
        if(!check()) throw 1;
}
catch()
{
}

अपवादों का उपयोग करके आप डेटा भी पास कर सकते हैं।


10
कृपया कभी भी अपनी परिभाषा जैसी चतुर बातें न करें, वे आमतौर पर साथी डेवलपर्स के लिए पढ़ने के लिए कोड को कठिन बनाते हैं। मैंने किसी को केस को ब्रेक के रूप में परिभाषित करते हुए देखा है; हेडर फ़ाइल में मामला, और इसे एक cpp फ़ाइल में स्विच में इस्तेमाल किया, जिससे दूसरों को घंटों तक आश्चर्य होता है कि केस स्टेटमेंट के बीच स्विच क्यों टूट जाता है। Grrr ...
माइकल

5
और जब आप मैक्रोज़ का नाम लेते हैं, तो आपको उन्हें मैक्रोज़ (यानी, सभी अपरकेस में) जैसा दिखना चाहिए। अन्यथा वह व्यक्ति जो किसी चर / कार्य / प्रकार / आदि का नाम देता है। नाम everसे बहुत दुखी होंगे ...
jamesdlin

5

मैं विशेष रूप से उपयोग करने के तरीके में नहीं हूँ breakयाreturn ऐसे मामले में । यह देखते हुए कि आम तौर पर जब हम ऐसी स्थिति का सामना कर रहे होते हैं, तो यह आमतौर पर तुलनात्मक रूप से लंबी विधि होती है।

यदि हमारे पास कई निकास बिंदु हैं, तो यह कठिनाइयों का कारण बन सकता है जब हम जानना चाहते हैं कि किस तर्क को निष्पादित करने का कारण होगा: आम तौर पर हम सिर्फ तर्क के उस टुकड़े को घेरे हुए ब्लॉक पर चलते रहते हैं, और उन घेरने वाले ब्लॉक के मानदंड हमें बताते हैं। परिस्थिति:

उदाहरण के लिए,

if (conditionA) {
    ....
    if (conditionB) {
        ....
        if (conditionC) {
            myLogic();
        }
    }
}

घेरने वाले ब्लॉकों को देखकर, यह पता लगाना आसान है कि myLogic()केवल तब ही होता हैconditionA and conditionB and conditionC यह सच होता है।

शुरुआती रिटर्न मिलने पर यह बहुत कम दिखाई देता है:

if (conditionA) {
    ....
    if (!conditionB) {
        return;
    }
    if (!conditionD) {
        return;
    }
    if (conditionC) {
        myLogic();
    }
}

अब हम इससे नेविगेट नहीं कर सकते myLogic() , स्थिति का पता लगाने के लिए संलग्न ब्लॉक को देख सकते हैं।

मेरे द्वारा उपयोग किए जाने वाले अलग-अलग वर्कअराउंड हैं। उनमें से एक यहां पर है:

if (conditionA) {
    isA = true;
    ....
}

if (isA && conditionB) {
    isB = true;
    ...
}

if (isB && conditionC) {
    isC = true;
    myLogic();
}

(बेशक यह सभी को बदलने के लिए एक ही चर का उपयोग करने के लिए स्वागत है isA isB isC ।)

ऐसा दृष्टिकोण कम से कम कोड के पाठक को देगा, myLogic()जिसे तब निष्पादित किया जाता है isB && conditionC। पाठक को एक संकेत दिया जाता है कि उसे आगे देखने की आवश्यकता है कि isB क्या सत्य है।


3
typedef bool (*Checker)();

Checker * checkers[]={
 &checker0,&checker1,.....,&checkerN,NULL
};

bool checker1(){
  if(condition){
    .....
    .....
    return true;
  }
  return false;
}

bool checker2(){
  if(condition){
    .....
    .....
    return true;
  }
  return false;
}

......

void doCheck(){
  Checker ** checker = checkers;
  while( *checker && (*checker)())
    checker++;
}

उस के बारे में कैसा है?


अगर यह अप्रचलित है, बस return condition;, अन्यथा मुझे लगता है कि यह अच्छी तरह से बनाए रखने योग्य है।
स्पेसट्रैकर

2

एक और पैटर्न उपयोगी है अगर आपको विफलता के आधार पर अलग-अलग क्लीनअप चरणों की आवश्यकता होती है:

    private ResultCode DoEverything()
    {
        ResultCode processResult = ResultCode.FAILURE;
        if (DoStep1() != ResultCode.SUCCESSFUL)
        {
            Step1FailureCleanup();
        }
        else if (DoStep2() != ResultCode.SUCCESSFUL)
        {
            Step2FailureCleanup();
            processResult = ResultCode.SPECIFIC_FAILURE;
        }
        else if (DoStep3() != ResultCode.SUCCESSFUL)
        {
            Step3FailureCleanup();
        }
        ...
        else
        {
            processResult = ResultCode.SUCCESSFUL;
        }
        return processResult;
    }

2

मैं C ++ प्रोग्रामर नहीं हूं, इसलिए मैं यहां कोई कोड नहीं लिखूंगा, लेकिन अभी तक किसी ने ऑब्जेक्ट ओरिएंटेड सॉल्यूशन का उल्लेख नहीं किया है। तो यहाँ मेरा अनुमान है कि:

एक सामान्य इंटरफ़ेस है जो किसी एकल स्थिति का मूल्यांकन करने के लिए एक विधि प्रदान करता है। अब आप अपने ऑब्जेक्ट में उन शर्तों के कार्यान्वयन की एक सूची का उपयोग कर सकते हैं जिनमें प्रश्न में विधि शामिल है। आप सूची पर पुनरावृति करते हैं और प्रत्येक स्थिति का मूल्यांकन करते हैं, यदि कोई विफल हो जाता है तो संभवतः जल्दी से बाहर निकल जाता है।

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

हालांकि, नकारात्मक पक्ष यह है कि अधिक वस्तुओं के उपयोग और सूची के पुनरावृत्ति के कारण इसमें कुछ अधिक ओवरहेड शामिल है।


क्या आप एक अलग भाषा में एक उदाहरण जोड़ सकते हैं? मुझे लगता है कि यह प्रश्न कई भाषाओं पर लागू होता है, हालांकि यह विशेष रूप से C ++ के बारे में पूछा गया था।
डेनिस स्किडमोर

1

यह मैं ऐसा कर रहा हूं।

void func() {
  if (!check()) return;
  ...
  ...

  if (!check()) return;
  ...
  ...

  if (!check()) return;
  ...
  ...
}

1

सबसे पहले, gotoC ++ के लिए एक अच्छा समाधान क्यों नहीं है यह दिखाने के लिए एक छोटा उदाहरण :

struct Bar {
    Bar();
};

extern bool check();

void foo()
{
    if (!check())
       goto out;

    Bar x;

    out:
}

इसे ऑब्जेक्ट फ़ाइल में संकलित करने का प्रयास करें और देखें कि क्या होता है। फिर कोशिश बराबर do+ break+while(0)

वह एक तरफ था। मुख्य बिंदु निम्नानुसार है।

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

इन शब्दार्थों को प्राप्त करने का एक विकल्प RAII है ; देखिए @ utnapistim का जवाब सी ++ गारंटी देता है कि स्वचालित विध्वंसक निर्माणकर्ताओं के विपरीत क्रम में चलते हैं, जो स्वाभाविक रूप से "अनइंडिंग" की आपूर्ति करता है।

लेकिन इसके लिए बहुत से आरएआई वर्ग की आवश्यकता होती है। कभी-कभी स्टैक का उपयोग करने के लिए एक सरल विकल्प होता है:

bool calc1()
{
    if (!check())
        return false;

    // ... Do stuff1 here ...

    if (!calc2()) {
        // ... Undo stuff1 here ...
        return false;
    }

    return true;
}

bool calc2()
{
    if (!check())
        return false;

    // ... Do stuff2 here ...

    if (!calc3()) {
        // ... Undo stuff2 here ...
        return false;
    }

    return true;
}

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

इसमें calcबहुत सारे तर्कों को लेने के लिए कार्यों की आवश्यकता हो सकती है , लेकिन यह आमतौर पर एक समस्या नहीं है यदि आपकी कक्षाओं / संरचनाओं में अच्छा सामंजस्य है। (अर्थात्, सामान जो एक साथ रहता है, एक ही वस्तु में रहता है, इसलिए ये फ़ंक्शन कम संख्या में ऑब्जेक्ट के लिए संकेत या संदर्भ ले सकते हैं और फिर भी बहुत सारे उपयोगी काम कर सकते हैं।)


सफाई पथ का ऑडिट करना बहुत आसान है, लेकिन शायद सुनहरे रास्ते का पता लगाना इतना आसान नहीं है। लेकिन कुल मिलाकर, मुझे लगता है कि ऐसा कुछ एक सुसंगत सफाई पैटर्न को प्रोत्साहित करता है।
डेनिस स्किडमोर

0

यदि आपके पास कोड है यदि if..else if..else स्टेटमेंट का एक लंबा ब्लॉक है, तो आप कोशिश कर सकते हैं Functorsया पूरे ब्लॉक को फिर से लिख सकते हैं याfunction pointers । यह हमेशा सही समाधान नहीं हो सकता है लेकिन अक्सर होता है।

http://www.cprogramming.com/tutorial/functors-function-objects-in-c++.html


सिद्धांत रूप में यह संभव है (और बुरा नहीं है), लेकिन स्पष्ट फ़ंक्शन ऑब्जेक्ट्स के साथ-साथ यह केवल प्रवाह को बहुत दृढ़ता से बाधित करता है। OTOH, यहाँ, लैम्बदास या साधारण नामित कार्यों का उपयोग अच्छा अभ्यास, कुशल और अच्छी तरह से पढ़ता है।
लेफ्टरनैबाउट

0

यहां प्रस्तुत किए जा रहे विभिन्न उत्तरों की संख्या से मैं चकित हूं। लेकिन, अंत में कोड में जो मुझे बदलना है (यानी इसे हटा देंdo-while(0) हैक या कुछ भी ), मैंने यहां बताए जा रहे किसी भी उत्तर से कुछ अलग किया और मैं उलझन में हूं कि किसी ने ऐसा क्यों सोचा। यहाँ मैंने क्या किया है:

प्रारंभिक कोड:

do {

    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
    if(!check()) break;
    ...
    ...
} while(0);

finishingUpStuff.

अभी:

finish(params)
{
  ...
  ...
}

if(!check()){
    finish(params);    
    return;
}
...
...
if(!check()){
    finish(params);    
    return;
}
...
...
if(!check()){
    finish(params);    
    return;
}
...
...

तो, यहाँ क्या किया गया है कि परिष्करण सामग्री को एक फ़ंक्शन में अलग कर दिया गया है और चीजें अचानक इतनी सरल और साफ हो गई हैं!

मुझे लगा कि यह समाधान ध्यान देने योग्य है, इसलिए इसे यहां प्रदान किया गया है।


0

इसे एक ifकथन में समेकित करें :

if(
    condition
    && other_condition
    && another_condition
    && yet_another_condition
    && ...
) {
        if (final_cond){
            //Do stuff
        } else {
            //Do other stuff
        }
}

यह जावा जैसी भाषाओं में उपयोग किया जाने वाला पैटर्न है जहां गोटो कीवर्ड हटा दिया गया था।


2
यह केवल तभी काम करता है जब आपको शर्त-परीक्षणों के बीच कोई सामान न करना पड़े। (अच्छी तरह से, मुझे लगता है कि आप हालत-परीक्षणों द्वारा किए गए कुछ फ़ंक्शन कॉल के अंदर कर रहे सामान को छिपा सकते हैं, लेकिन अगर आप इसे बहुत अधिक कर रहे हैं तो थोड़ा
बाधित

@JeremyFriesner वास्तव में, आप वास्तव में अलग-अलग बूलियन कार्यों के रूप में इन-द-सामान कर सकते हैं जो हमेशा के रूप में मूल्यांकन करते हैं true। शॉर्ट सर्किट मूल्यांकन यह गारंटी देता है कि आप कभी भी बीच-बीच में सामान नहीं चलाते हैं जिसके लिए सभी आवश्यक परीक्षण पास नहीं हुए हैं।
AJMansfield

@AJMansfield हाँ, यही मैं अपने दूसरे वाक्य में उल्लेख कर रहा था ... लेकिन मुझे यकीन नहीं है कि यह कोड गुणवत्ता में सुधार होगा।
जेरेमी फ्रेज़र

@JeremyFriesner कुछ भी आपको शर्तों को लिखने से रोकता है (/*do other stuff*/, /*next condition*/), आप इसे अच्छी तरह से प्रारूपित कर सकते हैं। बस लोगों को यह पसंद करने की उम्मीद नहीं है। लेकिन ईमानदारी से, यह केवल यह दिखाने के लिए जाता है कि जावा के लिए उस gotoकथन को चरणबद्ध करने के लिए यह एक गलती थी ...
cmaster - reicaate monica

@JeremyFriesner मैं यह मान रहा था कि ये बूलियन थे। यदि प्रत्येक स्थिति के अंदर फ़ंक्शन चलाए जा रहे थे, तो इसे संभालने का एक बेहतर तरीका है।
टाइजॉइड

0

यदि सभी त्रुटियों के लिए एक ही त्रुटि हैंडलर का उपयोग किया जाता है, और प्रत्येक चरण सफलता का संकेत देने वाला एक बूल लौटाता है:

if(
    DoSomething() &&
    DoSomethingElse() &&
    DoAThirdThing() )
{
    // do good condition action
}
else
{
    // handle error
}

(टाइजॉइड के उत्तर के समान है, लेकिन स्थितियां क्रियाएं हैं, और && पहले विफलता के बाद अतिरिक्त क्रियाओं को होने से रोकता है।)


0

झंडोत्तोलन विधि का उत्तर क्यों नहीं दिया गया इसका उपयोग युगों से किया जाता है।

//you can use something like this (pseudocode)
long var = 0;
if(condition)  flag a bit in var
if(condition)  flag another bit in var
if(condition)  flag another bit in var
............
if(var == certain number) {
Do the required task
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.