C ++ क्लास कंस्ट्रक्टर में विफलता के मामलों को कैसे संभालें?


21

मेरे पास एक CPP क्लास है जिसका कंस्ट्रक्टर कुछ ऑपरेशन करता है। इनमें से कुछ ऑपरेशन विफल हो सकते हैं। मुझे पता है कि कंस्ट्रक्टर कुछ भी वापस नहीं करते हैं।

मेरे प्रश्न हैं,

  1. क्या यह कुछ कार्यों को करने की अनुमति देता है जो एक निर्माणकर्ता में सदस्यों को शुरू करना है?

  2. क्या कॉलिंग फ़ंक्शन को यह बताना संभव है कि कंस्ट्रक्टर में कुछ ऑपरेशन विफल हो गए हैं?

  3. new ClassName()अगर कंस्ट्रक्टर में कुछ त्रुटियां होती हैं तो क्या मैं NULL लौटा सकता हूं ?


22
आप कंस्ट्रक्टर के भीतर से एक अपवाद फेंक सकते हैं। यह पूरी तरह से मान्य पैटर्न है।
एंडी

1
आपको गोफ के कुछ रचनात्‍मक प्रतिमानों पर नजर डालनी चाहिए । मैं फ़ैक्टरी पैटर्न की सलाह देता हूं।
स्पेसट्रैकर

2
# 1 का एक सामान्य उदाहरण डेटा सत्यापन है। IE यदि आपके पास एक वर्ग है Square, एक निर्माता के साथ जो एक पैरामीटर लेता है, तो एक पक्ष की लंबाई, आप जांचना चाहते हैं कि क्या मान 0. से अधिक है
डेविड ने कहा कि मोनिका

1
पहले सवाल के लिए, मैं आपको चेतावनी देता हूं कि वर्चुअल फ़ंक्शंस कंस्ट्रक्टर्स में एकतरफा व्यवहार कर सकते हैं। डिकंस्ट्रक्टर्स के साथ भी। ऐसे बुलाने से सावधान रहें।

1
# 3 - आप एक NULL क्यों लौटना चाहेंगे? OO के लाभों में से एक रिटर्न मानों की जांच करना नहीं है। बस उपयुक्त संभावित अपवादों को पकड़ें ()।
२०:५० पर MrWonderful

जवाबों:


42
  1. हां, हालांकि कुछ कोडिंग मानक इसे प्रतिबंधित कर सकते हैं।

  2. हाँ। अनुशंसित तरीका अपवाद को फेंकना है। वैकल्पिक रूप से, आप ऑब्जेक्ट के अंदर त्रुटि जानकारी संग्रहीत कर सकते हैं और इस जानकारी तक पहुंचने के तरीके प्रदान कर सकते हैं।

  3. नहीं।


4
जब तक कि ऑब्जेक्ट अभी भी वैध स्थिति में है, भले ही कंस्ट्रक्टर के कुछ हिस्से ने आवश्यकताओं को पूरा नहीं किया है और इस प्रकार एक त्रुटि के रूप में चिह्नित किया गया है, 2) वास्तव में ऐसा करने के लिए अनुशंसित नहीं है। यह तब बेहतर होता है जब कोई वस्तु या तो वैध अवस्था में मौजूद होती है या बिल्कुल मौजूद नहीं होती है।
एंडी

@DavidPacker सहमत, यहां देखें: stackoverflow.com/questions/77639/… लेकिन कुछ कोडिंग दिशानिर्देश अपवादों को प्रतिबंधित करते हैं, जो निर्माणकर्ताओं के लिए समस्याग्रस्त है।
सेबेस्टियन रेडल

किसी तरह मैं तुम्हें पहले ही उस जवाब के लिए अपटूस्ट दे दूंगा, सेबस्टियन। दिलचस्प। : डी
एंडी

10
@ योक्सी नहीं, यह नहीं है। आपके ओवरराइड किए गए नए को मेमोरी आवंटित करने के लिए कहा जाता है, लेकिन कंस्ट्रक्टर को कॉल आपके ऑपरेटर द्वारा वापस आने के बाद कंपाइलर द्वारा किया जाता है, जिसका अर्थ है कि आपको त्रुटि पकड़ने की आवश्यकता नहीं है। यह मानते हुए कि नया बिल्कुल कहा जाता है; यह स्टैक-आबंटित वस्तुओं के लिए नहीं है, जो उनमें से अधिकांश होना चाहिए।
सेबेस्टियन रेडल

1
# 1 के लिए, RAII एक सामान्य उदाहरण है, जहां कंस्ट्रक्टर में अधिक करना आवश्यक हो सकता है।
एरिक

20

आप एक स्थैतिक विधि बना सकते हैं जो गणना करता है या तो सफलता के मामले में एक वस्तु है या असफलता का मामला है।

ऑब्जेक्ट का यह निर्माण कैसे किया जाता है, इसके आधार पर, एक अन्य ऑब्जेक्ट बनाने के लिए बेहतर हो सकता है जो गैर-स्थिर विधि में ऑब्जेक्ट के निर्माण की अनुमति देता है।

एक निर्माता को अप्रत्यक्ष रूप से कॉल करने को अक्सर "कारखाना" कहा जाता है।

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


शुक्रिया @null! दुर्भाग्य से यहां दो उत्तर स्वीकार नहीं किए जा सकते हैं :( अन्यथा मैंने भी इस उत्तर को स्वीकार कर लिया होता !! धन्यवाद फिर से!
मयूर

@MayurK को कोई चिंता नहीं है, स्वीकृत उत्तर सही उत्तर को चिन्हित करना नहीं है , बल्कि आपके लिए काम करने वाला है।
null

3
@null: C ++ में, आप अभी वापस नहीं लौट सकते NULL। उदाहरण के लिए, int foo() { return NULLआप वास्तव में 0(शून्य), एक पूर्णांक वस्तु लौटाएंगे । में std::string foo() { return NULL; }अगर आप गलती से फोन करता हूँ std::string::string((const char*)NULL)जो अपरिभाषित व्यवहार है (शून्य एक \ 0-समाप्त स्ट्रिंग को इंगित नहीं करता है)।
MSALERS

3
std :: वैकल्पिक एक रास्ता हो सकता है लेकिन आप हमेशा बढ़ावा का उपयोग कर सकते हैं :: वैकल्पिक यदि आप इस तरह से जाना चाहते थे।
शॉन बर्टन

1
@ बड़ी: C ++ में, ऑब्जेक्ट्स क्लास के प्रकारों तक सीमित नहीं हैं। और सामान्य प्रोग्रामिंग के साथ, कारखानों के साथ समाप्त होने के लिए यह असामान्य नहीं है int। जैसे std::allocator<int>एक पूरी तरह से कारखाना है।
एमएसलटर्स

5

@SebastianRedl ने पहले ही सरल, सीधे उत्तर दिए, लेकिन कुछ अतिरिक्त स्पष्टीकरण उपयोगी हो सकते हैं।

TL; DR = कंस्ट्रक्टरों को सरल रखने के लिए एक शैली नियम है, इसके कारण हैं, लेकिन वे कारण ज्यादातर कोडिंग की एक ऐतिहासिक (या बस खराब) शैली से संबंधित हैं। कंस्ट्रक्टर्स में अपवादों की हैंडलिंग अच्छी तरह से परिभाषित है, और विध्वंसक अभी भी पूरी तरह से निर्मित स्थानीय चर और सदस्यों के लिए कॉल किए जाएंगे, जिसका अर्थ है कि मुहावरेदार सी ++ कोड में कोई समस्या नहीं होनी चाहिए। शैली का नियम वैसे भी बना रहता है, लेकिन आम तौर पर यह कोई समस्या नहीं है - सभी आरंभीकरण को कंस्ट्रक्टर में नहीं होना चाहिए, और विशेष रूप से जरूरी नहीं कि कंस्ट्रक्टर।


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

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

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

यदि कंस्ट्रक्टर के अंदर एक अपवाद फेंक दिया जाता है, तो कुछ भी भाग का निर्माण करने की आवश्यकता होती है, जिसे आम तौर पर साफ किया जाता है try .. catch

हालांकि, ऑब्जेक्ट के घटक जो पहले से सफलतापूर्वक निर्माण कर चुके हैं, वे पहले से ही संकलक जिम्मेदारी हैं। इसका मतलब है कि व्यवहार में, यह वास्तव में एक बड़ी बात नहीं है। जैसे

classname (args) : base1 (args), member2 (args), member3 (args)
{
}

इस कंस्ट्रक्टर का शरीर खाली है। इतने लंबे समय के लिए निर्माणकर्ताओं के रूप में base1, member2और member3अपवाद सुरक्षित हैं, चिंता की कोई बात नहीं है। उदाहरण के लिए, यदि कंस्ट्रक्टर member2थ्रो का है, तो वह कंस्ट्रक्टर खुद को साफ करने के लिए जिम्मेदार है। आधार base1पहले से ही पूरी तरह से निर्माण किया गया था, इसलिए इसका विध्वंसक स्वचालित रूप से कहा जाएगा। member3आंशिक रूप से निर्माण भी नहीं किया गया था, इसलिए सफाई की आवश्यकता नहीं है।

यहां तक ​​कि जब एक निकाय होता है, तो अपवाद को फेंकने से पहले पूरी तरह से बनाए गए स्थानीय चर स्वचालित रूप से किसी अन्य फ़ंक्शन की तरह नष्ट हो जाएंगे। कन्स्ट्रक्टर बॉडी जो कच्चे पॉइंटर्स को हथियाते हैं, या "स्वयं" किसी प्रकार की अंतर्निहित स्थिति (अन्यत्र संग्रहीत) - आम तौर पर एक शुरुआत / प्राप्त फ़ंक्शन कॉल का अंत / रिलीज़ कॉल से मिलान होना चाहिए - अपवाद सुरक्षा समस्याओं का कारण बन सकता है, लेकिन वास्तविक स्थिति एक वर्ग के माध्यम से एक संसाधन को ठीक से प्रबंधित करने में विफल हो रहा है। उदाहरण के लिए यदि आप unique_ptrनिर्माण में कच्चे पॉइंटर्स को प्रतिस्थापित करते हैं, unique_ptrतो आवश्यकता के लिए विध्वंसक को स्वचालित रूप से कहा जाएगा।

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

और निश्चित रूप से आप इसे एक make_whatever ()फ़ंक्शन में लपेट सकते हैं , इसलिए उस फ़ंक्शन के कॉल करने वालों को कमजोर-इनवेरिएंट क्लास उदाहरण देखने की आवश्यकता नहीं है।


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

इस प्रकार, "डू-द-मिनिमल" कंस्ट्रक्टर प्राइवेट हो सकता है, और "मेक_हैवरएवर ()" फंक्शन एक और कंस्ट्रक्टर हो सकता है जो प्राइवेट कॉल करता है।
बेन वोइगट

यह RAII की परिभाषा नहीं है जिससे मैं परिचित हूं। RAII की मेरी समझ किसी वस्तु के निर्माता में जानबूझकर (और केवल) एक संसाधन प्राप्त करना है और इसे अपने विध्वंसक में जारी करना है। इस तरह, ऑब्जेक्ट का उपयोग स्टैक पर स्वचालित रूप से अधिग्रहण और जारी किए गए संसाधनों के रिलीज को प्रबंधित करने के लिए किया जा सकता है। क्लासिक उदाहरण एक ताला है जो निर्माण होने पर म्यूटेक्स प्राप्त करता है और विनाश पर जारी करता है।
एरिक

1
@ एरिक - हाँ, यह बिल्कुल मानक अभ्यास है - एक मानक अभ्यास जिसे आमतौर पर RAII कहा जाता है। यह सिर्फ मैं नहीं हूं जो परिभाषा को फैलाता है - यह स्ट्रॉस्ट्रुप भी है, कुछ बातचीत में। हां, RAII संसाधन जीवनचक्र को वस्तु जीवनचक्र से जोड़ने के बारे में है, मानसिक मॉडल स्वामित्व है।
स्टीव 314

1
@ पिछला - पिछला उत्तर हटा दिया गया क्योंकि उन्हें बुरी तरह समझाया गया था। वैसे भी, वस्तुएं स्वयं संसाधन हैं जो स्वामित्व में हो सकते हैं। सब कुछ एक मालिक होना चाहिए , mainफ़ंक्शन या स्थिर / वैश्विक चर तक सही श्रृंखला में । एक ऑब्जेक्ट का उपयोग करके आवंटित किया गया है new, जब तक कि आप उस ज़िम्मेदारी को नहीं सौंपते हैं, लेकिन स्मार्ट पॉइंटर्स ढेर-आवंटित ऑब्जेक्ट्स को संदर्भित करते हैं, और वे आपके डेटा संरचनाओं के मालिक हैं। मालिक जल्दी हटाने के लिए चुन सकते हैं, मालिक विनाशकारी अंततः जिम्मेदार है।
स्टीव 314
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.