2009-07-09 17 views
13

मैं अभी सीए ++ में आरएआईआई के साथ शुरुआत कर रहा हूं और थोड़ा परीक्षण केस स्थापित कर रहा हूं। या तो मेरा कोड गहराई से उलझन में है, या आरएआईआई काम नहीं कर रहा है! (मुझे लगता है कि यह पूर्व है)।सी ++ आरएआईआई काम नहीं कर रहा है?

अगर मैं चलाएँ:

#include <exception> 
#include <iostream> 
class A { 
public: 
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; } 
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; } 
private: 
    int i_; 
}; 

int main(void) { 
    A a1(1); 
    A a2(2); 
    throw std::exception(); 
    return 0; 
} 
अपवाद बाहर टिप्पणी की मैं साथ

:

A 1 constructed 
A 2 constructed 
A 2 destructed 
A 1 destructed 

अपेक्षा के अनुरूप है, लेकिन अपवाद के साथ मैं:

A 1 constructed 
A 2 constructed 
terminate called after throwing an instance of 'std::exception' 
    what(): std::exception 
Aborted 
तो

मेरी वस्तुओं भले ही वे गुंजाइश से बाहर नहीं जा रहे हैं। क्या यह आरएआईआई के लिए पूरा आधार नहीं है।

पॉइंटर्स और सुधारों की बहुत सराहना की!

+5

आपको भी सी ++ में एक बग मिला! =) – Eric

+0

दिलचस्प किनारे का मामला! –

+2

आपने RAII तोड़ दिया :( – rpg

उत्तर

6

आपके पास मुख्य में एक अनचाहे अपवाद है, जिसका अर्थ है कि कॉल समाप्त हो जाए। इस प्रयास करें:

int main(void) 
{ 
    try 
    { 
     A a1(1); 
     A a2(2); 
     throw std::exception(); 
     return 0; 
    } 
    catch(const std::exception & e) 
    { 
     return 1; 
    } 


} 
+0

तो मुझे हमेशा RAII का उपयोग करते समय कोशिश/पकड़ में मुख्य रूप से लपेटना चाहिए? – John

+2

मुझे लगता है कि मुख्य() अलग है। सब कुछ डालने का प्रयास करें एक फ़ंक्शन जिसे आप मुख्य से कॉल करते हैं() –

+1

नहीं, केवल तभी जब आप ऐसा कुछ करते हैं जो अपवाद फेंक सकता है। – Alex

3

आप ठीक से अपवाद हैंडलिंग नहीं कर रहे हैं, तो इससे पहले कि वस्तुओं दायरे से बाहर जाने के अपने अनुप्रयोग से बाहर निकलते है।

मैं थोड़ा और समझाऊंगा। यदि मुख्य अपवाद के लिए अपवाद "बुलबुले" अवांछित है (संपादित करें)। कोड को द्वितीयक फ़ंक्शन में ले जाने से भी इस समस्या को ठीक नहीं किया जाएगा। पूर्व:

 1 #include <exception> 
     2 #include <iostream> 
     3 
     4 void test(); 
     5 
     6 class A { 
     7  public: 
     8   A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; } 
     9   ~A() { std::cout << "A " << i_ << " destructed" << std::endl; } 
    10  private: int i_; 
    11 }; 
    12 
    13 
    14 int main(void) { 
    15  test(); 
    16  return 0; 
    17 } 
    18 
    19 void test(){ 
    20    A a1(1); 
    21    A a2(2); 
    22   throw std::exception(); 
    23 } 

ऊपर कोड समस्या का समाधान नहीं होगा। इसे हल करने का एकमात्र तरीका एक कोशिश-पकड़ ब्लॉक में फेंक दिया अपवाद लपेटना है। यह अपवाद को मुख्य तक पहुंचने से रोक देगा, और वस्तुओं को गुंजाइश से बाहर होने से पहले होने वाली समाप्ति को रोक देगा।

+0

यह एक सही उत्तर है, -1? वास्तव में? – Alex

+0

यदि आप कोड को किसी अन्य फ़ंक्शन पर ले जाते हैं, तो यदि आपके पास कोशिश/पकड़ नहीं है तो यह मुख्य पर बबल होगा और उसी समस्या का कारण बन जाएगा। तो "मुख्य" बिल्कुल खास नहीं है। – Alex

+0

आप "अपरिभाषित व्यवहार" के बारे में गलत हैं। हैंडलर के बिना अपवाद का व्यवहार मानक में परिभाषित किया गया है, सिवाय इसके कि स्टैक अवांछित है या नहीं, कार्यान्वयन परिभाषित किया गया है। –

20

आपके पास अपवाद के लिए कोई हैंडलर नहीं है। जब ऐसा होता है तो मानक कहता है कि std :: terminate कहा जाता है, जो बदले में निरस्त करता है। सी ++ प्रोग्रामिंग भाषा, तीसरा संस्करण में सेक्शन 14.7 देखें।

+0

आप वास्तव में अपना खुद का समापन कार्य निर्धारित कर सकते हैं, लेकिन मुझे नहीं पता कि यह वास्तव में उपयोगी क्षमता है। –

+2

ठीक है, इसका उपयोग किसी विशिष्ट फ़ाइल को समाप्त करने या किसी को ईमेल भेजने के लिए कहा जा सकता है। –

+0

जैसा कि राफेलप्स ने इंगित किया है, यदि आप समाप्ति या क्लीनअप वैश्विक संसाधनों को लॉग करना चाहते हैं तो अपना स्वयं का समापन कार्य उपयोगी है। हालांकि, आप समाप्त होने से ठीक नहीं हो सकते हैं। एक समाप्ति हैंडलर कोई तर्क नहीं लेता है, कोई वापसी मूल्य नहीं है, और आवेदन से बाहर निकलने की उम्मीद है। –

17

समस्या यह है कि main एक विशेष स्थिति है। जब वहां से एक अपवाद फेंक दिया जाता है, तो स्टैक को अर्थपूर्ण रूप से अवांछित नहीं किया जा सकता है, एप्लिकेशन केवल std:terminate पर कॉल करता है।

और फिर यह थोड़ा सा समझ में आता है कि चर के दायरे से बाहर क्यों नहीं जाते हैं। हमने वास्तव में उस दायरे को नहीं छोड़ा है जिसमें उन्हें घोषित किया गया था।

int main(void) { 
    A a1(1); 
    A a2(2); 
    std::terminate(); 
} 

(मेरा मानना ​​है कि यह कार्यान्वयन-परिभाषित किया गया है कि क्या विनाशकर्ता हालांकि इस मामले में कहा जाता है, इसलिए कुछ प्लेटफार्मों पर, यह के रूप में आप की उम्मीद काम करेंगे)

: क्या होता है इस के बराबर होने का माना जा सकता है
+0

यह कार्यान्वयन क्यों परिभाषित किया गया है? अगर रचनाकारों को हमेशा बुलाया जाता है तो क्या यह अधिक समझ में नहीं आता है? सी ++ कभी-कभी मूर्खतापूर्ण हो सकता है ... – Zifre

+2

बेवकूफ नहीं - व्यावहारिक। हमेशा प्रशिक्षित करने वाले विनाशकों (रचनाकारों) को मजबूर करने के तरीकों को सीमित नहीं किया जाएगा कि ईएच और स्टैक अनलोलिंग लागू किया जा सकता है। –

+3

यह मूर्खतापूर्ण हो सकता है, लेकिन यह भी व्यावहारिक है। सी ++ spec कार्यान्वयन सीमित करने से बचने की कोशिश करता है। आदर्श रूप में, वे * किसी भी * सीपीयू और * किसी भी * ओएस पर सी ++ के मानक-अनुरूप कार्यान्वयन को बनाना संभव बनाना चाहते हैं। और चूंकि मुख्य कार्य एक अर्थ में ओएस और सी ++ के बीच की सीमा को चिह्नित करता है, इसलिए वे इसके बारे में ज्यादा कुछ नहीं मानना ​​चाहते हैं। आप सही हैं, असली दुनिया में, यह अच्छा होगा अगर उन्होंने इस विशेष विवरण को कार्यान्वयन तक नहीं छोड़ा। हम अब जानते हैं, लेकिन 1 99 8 में यह स्पष्ट था, जब भाषा का मानकीकरण किया जा रहा था? वे खुद को कोने में पेंट नहीं करना चाहते थे – jalf

5

जैसा कि अन्य ने बताया है, आपको एक अपवाद अपवाद मिला है, जिसे कॉल() कहते हैं। यह कार्यान्वयन-परिभाषित है (मानक, 15.3 अनुच्छेद 9 और 15.5.1 अनुच्छेद 2 देखें) क्या इस मामले में विनाशकों को बुलाया जाता है, और आपके कार्यान्वयन की परिभाषा जाहिर है "नहीं, वे नहीं करेंगे"। (यदि किसी अपवाद को फेंकने से किसी अन्य कारण के लिए समाप्त किया जाता है, जिसमें हैंडलर नहीं है, तो विनाशकों को नहीं बुलाया जाएगा।)

4

आपकी ऑब्जेक्ट्स नष्ट नहीं हो रही हैं क्योंकि std :: terminate कहा जा रहा है।

std :: टर्मिनेट को तब कहा जाता है जब एक अनचाहे अपवाद मुख्य से बाहर निकलता है। यदि आप अपना कोड किसी प्रयास/पकड़ में लपेटते हैं (भले ही पकड़ केवल फिर से उठाया जाए) तो आप उस व्यवहार को देखेंगे जिसकी आप अपेक्षा कर रहे थे।

6

यदि कोई अपवाद मुख्य से बच निकलता है() यह कार्यान्वयन परिभाषित मौसम है तो ढेर अवांछित है।

कोशिश

int main() 
{ 
    try 
    { 
     doWork(); // Do you experiment here. 
    } 
    catch(...) 
    { /* 
     * By catching here you force the stack to unwind correctly. 
     */ 
     throw; // re-throw so exceptions pass to the OS for debugging. 
    } 
} 
0

के बाद से अपवाद बार यह मुख्य तक पहुँच जाता है() के द्वारा नियंत्रित नहीं है, यह एक फोन में जो परिणाम एसटीडी :: समाप्त(), आप अनिवार्य रूप से

int main(void) { 
    A a1(1); 
    A a2(2); 
    exit(1); 
} 
के बराबर है

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

int main(void) { 
    A *a1 = new A(1); 
} 
+1

दूसरा उदाहरण आरएआईआई में छेद नहीं है; यह आरएआईआई का उपयोग न करने का एक उदाहरण है। –

1

दूसरों जो ठीक काम करता है इस संभाल करने के लिए, main() अंदर आज़माएं/कैच डालने का सुझाव दिया है। किसी कारण से मुझे शायद बेहतर दिखने के लिए शायद ही कभी 'फंक्शन-ट्राई-ब्लॉक' इस्तेमाल किया जाए, जो मुझे आश्चर्यचकित करता है (मैंने सोचा कि यह बहुत अजीब लगेगा)। लेकिन मैं वहाँ किसी भी असली लाभ नहीं लगता कि:

int main(void) 
try 
{ 
    A a1(1); 
    A a2(2); 
    throw std::exception(); 
    return 0; 
} 
catch (...) 
{ 
    throw; 
} 

नुकसान के एक जोड़े हैं कि क्योंकि यह शायद ही कभी प्रयोग किया जाता है डेवलपर्स के एक बहुत एक पाश के लिए फेंक दिया हो, जब वे यह देखते हैं, और VC6 यह पर chokes कि अगर एक है विचार।

0

निम्नलिखित कोड काम करता है।

#include <exception> 
#include <iostream> 

class A { 
public: 
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; } 
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; } 
private: 
    int i_; 
}; 

void test() { 
    A a1(1); 
    A a2(2); 
    throw std::exception(); 
} 

int main(void) { 
try { 
    test(); 
} catch(...) { 
} 
    return 0; 
} 
संबंधित मुद्दे