2009-07-07 32 views
8

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

class Exception 
{ 
public: 
    // construction 
    Exception(int code, const char* format="", ...); 
    virtual ~Exception(void); 

    <snip - get/set routines and print function> 

protected: 
private: 
    int mCode;    // thrower sets this 
    char mMessage[Exception::MessageLen]; // thrower says this FIXME: use String 
}; 

class Derived : public Exception { 
public: 
    Derived (const char* throwerSays) : Exception(1, throwerSays) {}; 
}; 

void innercall { 
    <do stuff> 
    throw Derived("Bad things happened!"); 
} 

void outercall { 
    try { 
    innercall(); 
    } 
    catch(Exception& e) 
    { 
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__); 
    throw e; 
    } 
} 

बग पाठ्यक्रम कि outercall समाप्त होता है एक अपवाद फेंकने की थी:

यहाँ हमारे आधार अपवाद वर्ग, एक व्युत्पन्न वर्ग, और प्रासंगिक कार्यों है। मेरी बग व्युत्पन्न विफलता को पकड़ने के लिए कॉल स्टैक प्रयासों में उच्च से अधिक परिणामस्वरूप हुई।

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

यदि हां, तो क्या मुझे उन वस्तुओं के लिए कॉपी कन्स्ट्रक्टर लॉक करने की अनुमति है जिन्हें फेंक दिया जाएगा? मैं वास्तव में यह फिर से नहीं करना पसंद करूंगा, और हमारे कोड के पास अपवाद वस्तुओं की प्रतिलिपि बनाने का कोई कारण नहीं है (जिसे मैं जानता हूं)।

कृपया, इस तथ्य पर कोई टिप्पणी नहीं कि हमारे पास अपना अपवाद पदानुक्रम है। यह एक पुराना डिजाइन है जिसे मैं सही करने के लिए काम कर रहा हूं (मैं अच्छी प्रगति कर रहा हूं। मुझे घर से उगाई जाने वाली स्ट्रिंग क्लास, और घर के कई कंटेनर से छुटकारा मिल गया है।)

अद्यतन: स्पष्ट होने के लिए, मैंने कभी सवाल पूछने से पहले बग तय किया था ('फेंक ई' को 'फेंक' में बदलकर)। मैं सिर्फ यह देखने की पुष्टि कर रहा था कि क्या हो रहा था।

+2

मुझे पता है कि आपने अन्य सामानों के बारे में चिंता न करने के लिए कहा है, लेकिन केवल दो अन्य चीजें हैं: अपनी 'अपवाद' वर्ग को 'std :: अपवाद' से प्राप्त करें और 'const &' द्वारा चीजें पकड़ें। – GManNickG

+0

कॉन्स के रूप में पकड़ने का क्या फायदा है? मैं हमेशा संदर्भ से पकड़ता हूं, लेकिन क्यों? (ऐसा नहीं है कि मैं इस समय इस कक्षा के साथ ऐसा कर सकता हूं, लेकिन एक दिन ...) –

+1

@ कोहेन: इस सेटिंग में स्थिरता का लाभ किसी भी अन्य सेटिंग में समान है - आप स्पष्ट रूप से उस इरादे को निर्दिष्ट करते हैं जिसे आप ' टी ऑब्जेक्ट की स्थिति को बदलने का इरादा रखता है और यह कि संकलक को यह सुनिश्चित करना चाहिए कि आप नहीं करते हैं - यह मानना ​​है कि अपरिवर्तनीय वस्तुओं के कारण विचार करना आसान है (यानी डिबग, परीक्षण, कोड सही साबित करें) क्योंकि वे संदर्भित रूप से लिखने में मदद करते हैं पारदर्शी कोड –

उत्तर

19

जब आप कोई ऑब्जेक्ट फेंकते हैं, तो आप वास्तव में ऑब्जेक्ट की एक प्रति फेंक रहे हैं, मूल नहीं। इसके बारे में सोचें - मूल वस्तु ढेर पर है, लेकिन ढेर अवांछित और अवैध है।

मुझे विश्वास है कि यह मानक का हिस्सा है, लेकिन मेरे पास संदर्भ में एक प्रति नहीं है।

कैच ब्लॉक में फेंकने का अपवाद कैच का मूल प्रकार है, न कि उस वस्तु का प्रकार जिसे फेंक दिया गया था। इस समस्या के आसपास throw;throw e; के बजाय जिस तरह से मूल पकड़ा अपवाद फेंक देगा।

+0

इससे संकेत मिलेगा कि मैं ऐसा कुछ नहीं फेंक सकता जिसके पास प्रतिलिपि बनाने वाला नहीं है? यह समझ आता है। –

10

A quick google बताता है कि हाँ, आप कॉपी कन्स्ट्रक्टर को फेंक रहे हैं और सार्वजनिक होना चाहिए। (जो के रूप में आप e की एक प्रति आरंभ कर रहे हैं और कि फेंकने, समझ में आता है।)

वैसे भी, सिर्फ अपवाद वस्तु निर्दिष्ट किए बिना throw उपयोग करते हैं, rethrow क्या catch में पकड़ा गया था। क्या समस्या को हल नहीं करना चाहिए?

catch(Exception& e) 
    { 
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__); 
    throw; 
    } 
+0

मुझे खेद है, हाँ, मैंने पहले से ही उस बग को ठीक से कर कर ठीक कर दिया है। मैं कॉपी कन्स्ट्रक्टर के बारे में पुष्टि प्राप्त करने की कोशिश कर रहा था। –

6

हां।

throw e; 

e की स्थिर प्रकार का एक अपवाद फेंकता है, जो कुछ भी e वास्तव में है परवाह किए बिना। इस मामले में, Derived अपवाद को प्रतिलिपि बनाने वाले का उपयोग करके Exception पर कॉपी किया गया है।

इस मामले में आप कर सकते हैं बस

throw; 

सही ढंग से Derived अपवाद बुलबुला उठना।

यदि आप कुछ अन्य मामलों में बहुलक फेंकने में रूचि रखते हैं, तो always so useful C++ FAQ Lite देखें।

1

सी ++ कभी मुझे आश्चर्यचकित नहीं करता है। मैं बहुत सारा पैसा खो देता था यह व्यवहार था कि व्यवहार क्या था!

अपवाद वस्तु पहली बार अस्थायी रूप से कॉपी की गई है और आपको throw का उपयोग करना चाहिए था। मानक 15.1/3 के शब्दों में:

एक थ्रो-अभिव्यक्ति एक अस्थायी वस्तु initializes, अपवाद वस्तु कहा जाता है, जो के प्रकार के संकार्य के स्थिर प्रकार से किसी भी शीर्ष स्तर के सीवी-क्वालिफायर को हटाने के द्वारा निर्धारित किया जाता है क्रमशः "टी की सरणी" या "फ़ंक्शन रिटर्निंग टी" से "पॉइंटर टू टी" या "फ़ंक्शन रिटर्निंग टी के सूचक" से क्रमशः फेंकने और समायोजित करने के लिए।

मैं यह एक बहुत ही उपयोगी कोडिंग मानक नियम को जन्म देता है लगता है:

एक अपवाद पदानुक्रम के

बेस कक्षाएं एक शुद्ध आभासी नाशक होना चाहिए।

या

एक अपवाद पदानुक्रम में एक आधार वर्ग के लिए प्रति निर्माता संरक्षित किया जाएगा।

या तो लक्ष्य है कि संकलक जब आप के बाद से पहले मामले में आप क्योंकि आप प्रतिलिपि निर्माता फोन नहीं कर सकते हैं एक अमूर्त वर्ग का एक उदाहरण और दूसरा नहीं बना सकते "ई फेंक" करने की कोशिश चेतावनी देगा प्राप्त होता है।

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