2012-10-09 13 views
6

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

void ControlLoop::main_loop() 
{ 
    InitializeAndCheckHardware(pHardware) //pHardware is a pointer given from outside 
    //The main loop 
    while (m_bIsRunning) 
    { 
     simulated_time += time_increment; //this will probably be += 0.001 seconds 
     ReadSensorData(); 
     if (data_is_bad) { 
      m_bIsRunning = false; 
      goto loop_end; 
     }  
     ApplyFilterToData(); 
     ComputeControllerOutput(); 
     SendOutputToHardware(); 
     ProcessPendingEvents(); 

     while (GetWallClockTime() < simulated_time) {} 
     if (end_condition_is_satisified) m_bIsRunning = false; 
    } 
    loop_end: 
    DeInitializeHardware(pHardware); 
} 

pHardware सूचक ControlLoop वस्तु बाहर से पारित कर दिया है और एक बहुरूपी प्रकार है, तो यह बहुत मतलब नहीं है मुझे का उपयोग करने के लिए: इस प्रकार स्यूडोकोड में धागे के सामान्य प्रवाह है RAII और main_loop के अंदर हार्डवेयर इंटरफ़ेस को स्वयं बनाने और नष्ट करने के लिए। मुझे लगता है कि मैं pHardware हार्डवेयर के "सत्र" या "उपयोग" का प्रतिनिधित्व करने वाली एक अस्थायी वस्तु बना सकता हूं जिसे मुख्य_लोप से बाहर निकलने पर स्वचालित रूप से साफ़ किया जा सकता है, लेकिन मुझे यकीन नहीं है कि वह विचार किसी को स्पष्ट करेगा और मेरा इरादा क्या है। लूप से केवल तीन तरीके ही होंगे: पहला यह है कि बाहरी हार्डवेयर से खराब डेटा पढ़ा जाता है; दूसरा यह है कि यदि ProcessPendingEvents() उपयोगकर्ता द्वारा शुरू की गई गर्भपात इंगित करता है, जो केवल m_bIs को झूठी बनने का कारण बनता है; और अंतिम यह है कि अंतराल लूप के नीचे संतुष्ट है। मुझे यह भी ध्यान रखना चाहिए कि main_loop को ControlLoop ऑब्जेक्ट के जीवन में कई बार शुरू किया जा सकता है और इसे समाप्त कर दिया जा सकता है, इसलिए इसे m_bIsRunning = false के बाद साफ से बाहर निकलना चाहिए।

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

अब प्रश्न के लिए: क्या यह समाधान आपको अपने कोड में लिखने के लिए असहज बनाता है? यह मेरे लिए थोड़ा गलत महसूस करता है, लेकिन फिर मैंने सी ++ कोड में पहले गोटो स्टेटमेंट का उपयोग नहीं किया है - इसलिए विशेषज्ञों की मदद के लिए मेरा अनुरोध। क्या कोई अन्य बुनियादी विचार है जो मुझे याद आ रहा है जो इस कोड को स्पष्ट कर देगा?

धन्यवाद।

+4

"यह मेरे लिए आरएआईआई का उपयोग करने के लिए ज्यादा समझ में नहीं आता है" यह हमेशा आरएआईआई का उपयोग करने के लिए समझ में आता है। हमेशा। समय के सभी। –

+10

यहां 'ब्रेक' का उपयोग क्यों न करें? –

+3

यदि आप केवल एक लूप है जिसे आप तोड़ रहे हैं, तो 'ब्रेक' का उपयोग करें; यह क्लीनर और स्पष्ट है। यदि आपके पास लूपिंग के कई स्तर हैं, या यदि आप 'स्विच' के अंदर हैं, और आपको सभी लूप से बाहर निकलने की आवश्यकता है, तो 'गोटो' ठीक है। –

उत्तर

3

अपने एक, एकवचन शर्त है जो पाश को तोड़ने के लिए जल्दी मैं बस एक प्रयोग करेंगे का कारण बनता है के साथ breakgoto की कोई आवश्यकता नहीं है जो break है।

हालांकि, उन समारोह कॉल की अगर किसी भी एक अपवाद फेंक कर सकते हैं या यदि आप अंत की आवश्यकता होगी, कई break रों मैं एक आरए II शैली कंटेनर पसंद करते हैं, इस बात का सही तरह विनाशकर्ता के लिए कर रहे हैं। तुम हमेशा DeInitializeHardware करने के लिए कॉल करते हैं, तो ...

// todo: add error checking if needed 
class HardwareWrapper { 
public: 
    HardwareWrapper(Hardware *pH) 
     : _pHardware(pH) { 
     InitializeAndCheckHardware(_pHardware); 
    } 

    ~HardwareWrapper() { 
     DeInitializeHardware(_pHardware); 
    } 

    const Hardware *getHardware() const { 
     return _pHardware; 
    } 

    const Hardware *operator->() const { 
     return _pHardware; 
    } 

    const Hardware& operator*() const { 
     return *_pHardware; 
    } 

private: 
    Hardware *_pHardware; 
    // if you don't want to allow copies... 
    HardwareWrapper(const HardwareWrapper &other); 
    HardwareWrapper& operator=(const HardwareWrapper &other); 
} 

// ... 

void ControlLoop::main_loop() 
{ 
    HardwareWrapper hw(pHardware); 
    // code 
} 

अब, कोई फर्क नहीं पड़ता कि क्या होता है, तो आप हमेशा DeInitializeHardware जब कि समारोह रिटर्न कॉल करेंगे।

+0

यह समाधान बहुत साफ है। धन्यवाद। – Hunter

+0

यह देखते हुए कि 'प्रारंभिक XXX' फ़ंक्शन है, मैं समरूपता के लिए, निर्माता में प्रारंभ करने का सुझाव दूंगा। –

5

goto के उपयोग से बचने के लिए सामान्य रूप से ऑब्जेक्ट उन्मुख विकास में एक बहुत ही ठोस बात है।

अपने मामले में, लूप से बाहर निकलने के लिए केवल break का उपयोग क्यों न करें?

while (true) 
{ 
    if (condition_is_met) 
    { 
     // cleanup 
     break; 
    } 
} 

अपने प्रश्न के लिए के रूप में: goto के आपके उपयोग मुझे परेशान होगा। एकमात्र कारण यह है कि break कम पढ़ा जा सकता है आपका मजबूत सी ++ डेवलपर नहीं होने का प्रवेश है। सी-जैसी भाषा के किसी भी अनुभवी डेवलपर को, break दोनों बेहतर पढ़ेंगे, साथ ही goto से क्लीनर समाधान प्रदान करेंगे।

विशेष रूप से, मैं बस सहमत नहीं हूँ कि

if (something) 
{ 
    goto loop_end; 
} 

से

if (something) 
{ 
    break; 
} 

जिसका शाब्दिक निर्मित वाक्य रचना के साथ एक ही बात कहते हैं और अधिक पठनीय है।

+0

मेरी मुख्य चिंता यह थी कि वास्तविक कोड में मुख्य समय लूप बहुत लंबा है, और इसलिए मेरी सोच यह थी कि ब्रेक का उपयोग करके यह थोड़ा कम स्पष्ट हो जाता है जहां कार्यक्रम प्रवाह तुरंत इसे पढ़ने पर जाएगा। हालांकि, आपकी टिप्पणी के लिए धन्यवाद। यह एक उचित दृष्टिकोण की तरह लगता है। – Hunter

+0

जब तक आपके पास अपने कोड में अजीब अंतर न हो, तब तक आपके कोड को किसी भी अनुभवी डेवलपर को 'while {}' ब्रेसिज़ का उपयोग करके काफी आसानी से प्रवाह करना चाहिए। लूप के बाहर के कम से कम का पालन करना विशेष रूप से आसान है। – pickypg

+0

इसके अलावा, एक आईडीई के लिए आपको यह बताने के लिए संभव है कि 'ब्रेक' कहाँ जाएगा (हालांकि मुझे नहीं पता कि आईडीई हैं जिनमें विशेष सुविधा है)। –

1

goto का उपयोग करने के बजाय, आपको लूप से बचने के लिए break; का उपयोग करना चाहिए।

+1

लूप के एक स्तर के लिए, यह सच है। पाश के कई स्तरों के बारे में क्या? –

+1

पोस्टर उस एकल लूप से बाहर निकलने के लिए गोटो का उपयोग कर रहा था, इसलिए ओपी के मामले में ब्रेक सबसे अच्छा (आईएमओ) विकल्प है। – zachjs

+1

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

3

अद्यतन

रहा है, तो अपने मुख्य चिंता है, जबकि पाश बहुत लंबा है, तो आप पर लक्ष्य रखना चाहिए इसे छोटा बनाएं, सी ++ एक OO भाषा है और OO, यहां तक ​​कि छोटे टुकड़े और घटक के विभाजन बातों के लिए है सामान्य गैर-ओओ भाषा में हम आम तौर पर अभी भी सोचते हैं कि हमें एक विधि/लूप को छोटे से तोड़ना चाहिए और इसे पढ़ने के लिए इसे आसान बनाना चाहिए। यदि लूप में 300 लाइनें हैं, तो कोई फर्क नहीं पड़ता कि ब्रेक/गोटो वास्तव में आपका समय नहीं बचाता है, है ना?

अद्यतन

मैं गोटो के खिलाफ नहीं हूँ लेकिन मैं इसे यहाँ का उपयोग नहीं होगा, जैसा कि आप करते हैं, मैं तो बस, तोड़ का उपयोग पसंद करते हैं आम तौर पर एक डेवलपर है कि वह एक तोड़ देखा वहां वह जानता है कि इसका मतलब है कि थोड़ी देर के अंत तक, और m_bIsRunning = false वह आसानी से अवगत हो सकता है कि यह वास्तव में सेकंड के भीतर लूप से बाहर निकलता है। हां गोटो सेकंड को समझने के लिए समय बचा सकता है लेकिन यह लोगों को आपके कोड के बारे में परेशान भी कर सकता है।

बात मैं कल्पना कर सकते हैं कि मैं एक गोटो उपयोग कर रहा हूँ एक दो स्तर पाश बाहर निकलने के लिए होगा:

while(running) 
{ 
    ... 
    while(runnning2) 
    { 
     if(bad_data) 
     { 
      goto loop_end; 
     } 
    } 
    ... 
} 
loop_end: 
+0

धन्यवाद, साइमन। मुख्य पाश हास्यास्पद रूप से लंबी (~ 150 लाइनें) नहीं है, लेकिन कम से कम मेरे लिए एक स्क्रीन से अधिक है। मैं निश्चित रूप से इसे और तोड़ सकता था। मैंने पहले से ही इस डिग्री को किया है जो मैंने उचित सोचा था, हालांकि। यह किसी अन्य प्रश्न में हो सकता है, और यदि ऐसा उत्तर देने में संकोच न हो, लेकिन क्या आप इस तरह के सबराउटिन क्लास विधियों को कक्षा में डेटा संग्रहीत करके संवाद करेंगे, या केवल उसी अनुवाद इकाई में कार्य करेंगे जो आउटपुट के लिए तर्क लेते हैं। – Hunter

+0

ठीक है, यह वास्तव में निर्भर करता है, और मुझे नहीं पता कि मैं इसे तब तक क्या करूँगा जब तक कि मैंने इसे नहीं बनाया;) जिस नियम का मैं मुख्य रूप से पालन करता हूं वह प्रत्येक वर्ग को केवल एक चीज के साथ एक क्षेत्र पर केंद्रित करेगा, हालांकि कोई भी सही नहीं है और मैं कुछ बदसूरत कोडिंग कर सकता है जिसे मैं नहीं जानता। मैं कहूंगा कि यहां कोड छोड़ दें क्योंकि यह है कि यदि आप और आपकी टीम सभी उस गेटो के साथ ठीक महसूस करते हैं और कोई भी इसे हटाना नहीं चाहता है। –

0

break एक विकल्प है जब लूप से निकलने के लिए गोटो अच्छा अभ्यास नहीं है।

इसके अलावा, जटिल दिनचर्या में, अंत में केवल एक निकास तर्क (सफाई के साथ) रखना अच्छा होता है। गोटो को कभी-कभी वापसी तर्क पर कूदने के लिए उपयोग किया जाता है। QEMU vmdk ब्लॉक ड्राइवर से

उदाहरण:

static int vmdk_open(BlockDriverState *bs, int flags) 
{ 
    int ret; 
    BDRVVmdkState *s = bs->opaque; 

    if (vmdk_open_sparse(bs, bs->file, flags) == 0) { 
     s->desc_offset = 0x200; 
    } else { 
     ret = vmdk_open_desc_file(bs, flags, 0); 
     if (ret) { 
      goto fail; 
     } 
    } 
    /* try to open parent images, if exist */ 
    ret = vmdk_parent_open(bs); 
    if (ret) { 
     goto fail; 
    } 
    s->parent_cid = vmdk_read_cid(bs, 1); 
    qemu_co_mutex_init(&s->lock); 

    /* Disable migration when VMDK images are used */ 
    error_set(&s->migration_blocker, 
       QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, 
       "vmdk", bs->device_name, "live migration"); 
    migrate_add_blocker(s->migration_blocker); 

    return 0; 

fail: 
    vmdk_free_extents(bs); 
    return ret; 
} 
1

वहाँ goto करने के लिए कई विकल्प हैं: break, continue और return स्थिति के आधार पर।

हालांकि, आपको यह ध्यान रखना होगा कि break और continue दोनों सीमित हैं कि वे केवल सबसे आंतरिक पाश को प्रभावित करते हैं। दूसरी ओर return इस सीमा से प्रभावित नहीं है।

सामान्य तौर पर, यदि आप एक gotoबाहर निकलने के लिए एक विशेष दायरे का उपयोग करें, तो आपको एक और समारोह और एक return बयान के बजाय का उपयोग कर refactor कर सकते हैं।

// Original 
void foo() { 
    DoSetup(); 
    while (...) { 
     for (;;) { 
      if() { 
       goto X; 
      } 
     } 
    } 
    label X: DoTearDown(); 
} 

// Refactored 
void foo_in() { 
    while (...) { 
     for (;;) { 
      if() { 
       return; 
      } 
     } 
    } 
} 

void foo() { 
    DoSetup(); 
    foo_in(); 
    DoTearDown(); 
} 

नोट:: यह है कि यह कोड आसान हो जाएगा एक बोनस के रूप पढ़े जाने की संभावना है अगर आपके समारोह शरीर अपने स्क्रीन में आसानी से फिट नहीं कर सकते, तो आप गलत कर रहे हैं।

+0

धन्यवाद, यह एक और समाधान है जो निश्चित रूप से विचार करने लायक है। – Hunter

0

मैं goto के बजाय break का सुझाव देने वाले लोगों के भार देख रहा हूं। लेकिन breakgoto से कोई "बेहतर" (या "खराब") नहीं है।

goto के खिलाफ न्यायिक जांच को प्रभावी ढंग से डिज्कस्ट्रा के "Go To Considered Harmful" paper वापस 1968 में, जब स्पेगेटी कोड नियम और जैसी चीजों के साथ शुरू किया था ब्लॉक-संरचना if और while बयान अभी भी अत्याधुनिक विचार किया गया। एएलजीओएल 60 ने उन्हें किया था, लेकिन यह अनिवार्य रूप से अकादमिकों द्वारा उपयोग की जाने वाली एक शोध भाषा थी (आज सीएफ एमएल); फोरट्रान, उस समय की प्रमुख भाषाओं में से एक, उन्हें 9 साल तक नहीं मिलेगा!

डिज्कस्ट्रा के समाचार पत्र में मुख्य बिंदु इस प्रकार हैं:

  1. मनुष्य स्थानिक तर्क में अच्छे हैं, और ब्लॉक-संरचना प्रोग्राम हैं जो को भुनाने क्योंकि कार्यक्रम कार्यों कि कुछ ही समय में एक दूसरे के निकट होते हैं में एक दूसरे के निकट वर्णित हैं " अंतरिक्ष "(प्रोग्राम कोड);
  2. यदि आप अपने सभी विभिन्न रूपों में goto से बचते हैं, तो को प्रोग्राम में प्रत्येक शब्दावली स्थिति में चर के संभावित राज्यों के बारे में जानना संभव है। विशेष रूप से, while लूप के अंत में, आप जानते हैं कि लूप की स्थिति गलत होनी चाहिए। यह डिबगिंग के लिए उपयोगी है। (डिज्कस्ट्रा काफी यह कहना नहीं है, लेकिन आप इसे अनुमान लगा सकते हैं।)

break, goto जैसे (और जल्दी return है, और अपवाद ...), कम कर देता है (1) और को हटा देता है (2)। बेशक, break का उपयोग करके आप while स्थिति के लिए घुलनशील तर्क लिखने से बचते हैं, जिससे आपको समझ में शुद्ध लाभ मिल जाता है - और वास्तव में goto के लिए भी लागू होता है।

+0

स्पष्टीकरण के लिए धन्यवाद। यह जानना भी अच्छा है कि मेरे विशिष्ट मामले में 'ब्रेक' या 'गोटो' के बिना, शेष समय के लूप को अतिरिक्त 'if' द्वारा संरक्षित करने की आवश्यकता होगी, क्योंकि खराब इनपुट के आधार पर हार्डवेयर को आउटपुट भेजना विनाशकारी हो सकता है , यानी खराब डेटा की सही प्रतिक्रिया "अभी रोकें" है। – Hunter

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