मुझे आश्चर्य है कि किसी ने भी इस विकल्प का सुझाव नहीं दिया है, इसलिए भले ही प्रश्न लगभग थोड़ी देर के लिए हो, मैं इसे इसमें जोड़ दूंगा: इस मुद्दे को संबोधित करने का एक अच्छा तरीका मौजूदा राज्य का ट्रैक रखने के लिए चर का उपयोग करना है। यह एक ऐसी तकनीक goto
है जिसका उपयोग सफाई कोड में पहुंचने के लिए किया जा सकता है या नहीं । किसी भी कोडिंग तकनीक की तरह, इसमें पेशेवरों और विपक्ष हैं, और यह हर स्थिति के लिए उपयुक्त नहीं होगा, लेकिन यदि आप एक ऐसी शैली का चयन कर रहे हैं, जो विचार करने योग्य है - खासकर यदि आप goto
गहरी नेस्टेड के बिना समाप्त होने से बचना चाहते हैंif
एस के ।
मूल विचार यह है कि, प्रत्येक क्लीनअप कार्रवाई के लिए जिसे लेने की आवश्यकता हो सकती है, एक चर है, जिसके मूल्य से हम बता सकते हैं कि सफाई को करने की आवश्यकता है या नहीं।
मैं goto
पहले संस्करण दिखाऊंगा , क्योंकि यह मूल प्रश्न में कोड के करीब है।
int foo(int bar)
{
int return_value = 0;
int something_done = 0;
int stuff_inited = 0;
int stuff_prepared = 0;
/*
* Prepare
*/
if (do_something(bar)) {
something_done = 1;
} else {
goto cleanup;
}
if (init_stuff(bar)) {
stuff_inited = 1;
} else {
goto cleanup;
}
if (prepare_stuff(bar)) {
stufF_prepared = 1;
} else {
goto cleanup;
}
/*
* Do the thing
*/
return_value = do_the_thing(bar);
/*
* Clean up
*/
cleanup:
if (stuff_prepared) {
unprepare_stuff();
}
if (stuff_inited) {
uninit_stuff();
}
if (something_done) {
undo_something();
}
return return_value;
}
अन्य तकनीकों में से कुछ पर इसका एक फायदा यह है कि, यदि आरंभीकरण कार्यों के क्रम को बदल दिया जाता है, तो सही सफाई अभी भी होगी - उदाहरण के लिए, switch
किसी अन्य उत्तर में वर्णित विधि का उपयोग करके , यदि आरंभीकरण का क्रम बदलता है, तो switch
वास्तव में पहली जगह में शुरू में कुछ साफ करने की कोशिश करने से बचने के लिए बहुत सावधानी से संपादित किया जाना चाहिए।
अब, कुछ का तर्क हो सकता है कि यह विधि कई अतिरिक्त चर जोड़ती है - और वास्तव में इस मामले में यह सच है - लेकिन व्यवहार में अक्सर एक मौजूदा चर पहले से ही ट्रैक करता है, या आवश्यक स्थिति को ट्रैक करने के लिए बनाया जा सकता है। उदाहरण के लिए, यदि prepare_stuff()
वास्तव में कॉल है malloc()
, या open()
फिर रिटर्नेड पॉइंटर या फाइल डिस्क्रिप्टर रखने वाले वेरिएबल का उपयोग किया जा सकता है - उदाहरण के लिए:
int fd = -1;
....
fd = open(...);
if (fd == -1) {
goto cleanup;
}
...
cleanup:
if (fd != -1) {
close(fd);
}
अब, अगर हम अतिरिक्त रूप से त्रुटि स्थिति को एक चर के साथ ट्रैक करते हैं, तो हम goto
पूरी तरह से बच सकते हैं , और फिर भी सही ढंग से सफाई कर सकते हैं, बिना इंडेंटेशन के जो हमें और अधिक प्रारंभिकता को गहरा और गहरा करता है, जिसकी हमें आवश्यकता है:
int foo(int bar)
{
int return_value = 0;
int something_done = 0;
int stuff_inited = 0;
int stuff_prepared = 0;
int oksofar = 1;
/*
* Prepare
*/
if (oksofar) { /* NB This "if" statement is optional (it always executes) but included for consistency */
if (do_something(bar)) {
something_done = 1;
} else {
oksofar = 0;
}
}
if (oksofar) {
if (init_stuff(bar)) {
stuff_inited = 1;
} else {
oksofar = 0;
}
}
if (oksofar) {
if (prepare_stuff(bar)) {
stuff_prepared = 1;
} else {
oksofar = 0;
}
}
/*
* Do the thing
*/
if (oksofar) {
return_value = do_the_thing(bar);
}
/*
* Clean up
*/
if (stuff_prepared) {
unprepare_stuff();
}
if (stuff_inited) {
uninit_stuff();
}
if (something_done) {
undo_something();
}
return return_value;
}
फिर, इसकी संभावित आलोचनाएँ हैं:
- क्या उन सभी को "यदि" प्रदर्शन में चोट नहीं लगी है? नहीं - क्योंकि सफलता के मामले में, आपको वैसे भी सभी जांच करनी होगी (अन्यथा आप सभी त्रुटि मामलों की जाँच नहीं कर रहे हैं); और विफलता के मामले में अधिकांश संकलक विफलता के अनुक्रम का अनुकूलन करेंगे
if (oksofar)
क्लीन जुकाम कोड (जीसीसी निश्चित रूप से करता है) के लिए एक एकल कूद चेक - और किसी भी मामले में, त्रुटि का मामला आमतौर पर प्रदर्शन के लिए कम महत्वपूर्ण होता है।
क्या यह अभी तक एक और चर नहीं जोड़ रहा है? इस मामले में हां, लेकिन अक्सर return_value
चर का उपयोग उस भूमिका को निभाने के लिए किया जा सकता oksofar
है जो यहां खेल रही है। यदि आप लगातार तरीके से त्रुटियों को वापस करने के लिए अपने कार्यों की संरचना करते हैं, तो आप if
प्रत्येक मामले में दूसरे से भी बच सकते हैं :
int return_value = 0;
if (!return_value) {
return_value = do_something(bar);
}
if (!return_value) {
return_value = init_stuff(bar);
}
if (!return_value) {
return_value = prepare_stuff(bar);
}
कोडिंग के फायदों में से एक यह है कि संगति का अर्थ है कि कोई भी स्थान जहां मूल प्रोग्रामर रिटर्न मान की जांच करना भूल गया है, एक गले में अंगूठे की तरह चिपक जाता है, जिससे उसे (उस एक वर्ग) कीड़े को ढूंढना बहुत आसान हो जाता है।
तो - यह (अभी तक) एक और शैली है जिसका उपयोग इस समस्या को हल करने के लिए किया जा सकता है। सही ढंग से उपयोग किया गया यह बहुत साफ, सुसंगत कोड के लिए अनुमति देता है - और किसी भी तकनीक की तरह, गलत हाथों में यह लंबे समय से घुमावदार और भ्रमित करने वाले कोड का उत्पादन कर सकता है :-)