c ++ अपवाद: std फेंकना :: स्ट्रिंग


80

मैं एक अपवाद फेंकना चाहूंगा जब मेरे सी ++ तरीके कुछ अजीब से सामना करेंगे और ठीक नहीं हो सकते। क्या std::stringपॉइंटर फेंकना ठीक है ?

यहाँ मैं क्या करने के लिए तत्पर था:

void Foo::Bar() {
    if(!QueryPerformanceTimer(&m_baz)) {
        throw new std::string("it's the end of the world!");
    }
}

void Foo::Caller() {
    try {
        this->Bar(); // should throw
    }
    catch(std::string *caught) { // not quite sure the syntax is OK here...
        std::cout << "Got " << caught << std::endl;
    }
}

23
यह कानूनी होगा, लेकिन नैतिक नहीं।
मार्सिन

18
आपके पास स्मृति रिसाव है। स्ट्रिंग पॉइंटर को कौन हटा रहा है? अपवादों के लिए पॉइंटर्स का उपयोग न करें।
fnieto - फर्नांडो नीटो

2
मैं जानता हूँ कि यह थोड़ी देर हो चुकी है, लेकिन वैसे भी, इस लेख इस मुद्दे पर अंक की एक संख्या है boost.org/community/error_handling.html
एलेक्स Kreimer

जवाबों:


100

हाँ। std::exceptionC ++ मानक पुस्तकालय में आधार अपवाद वर्ग है। आप अपवाद वर्ग के रूप में स्ट्रिंग के उपयोग से बचना चाह सकते हैं क्योंकि वे स्वयं उपयोग के दौरान अपवाद फेंक सकते हैं। अगर ऐसा हुआ, तो आप कहां होंगे?

अपवाद और त्रुटि से निपटने के लिए अच्छी शैली पर बूस्ट का एक उत्कृष्ट दस्तावेज है । यह पढ़ने लायक है।


20
साइड नोट: std :: टर्मिनेशन तब कहा जाएगा जब अपवाद ऑब्जेक्ट खुद फेंकता है, यही वह जगह है जहाँ आप होंगे (और यह बहुत अच्छा नहीं है!)
अलरिक

6
एक तर्क के लिए gotw.ca/publications/mill16.htm देखें कि अपवादों को फेंकने के साथ परेशान करना समय की बर्बादी क्यों है। एक और तर्क इस जवाब पर सहमत है कि std :: runtime_exception और परिवार इसे करता है, तो आप क्यों नहीं?
ग्रेग रोजर्स

63

कुछ सिद्धांत:

  1. आपके पास एक std :: अपवाद बेस क्लास है, आपके पास इसके अपवाद होने चाहिए। इस तरह सामान्य अपवाद हैंडलर के पास अभी भी कुछ जानकारी है।

  2. पॉइंटर्स, ऑब्जेक्ट को न फेंकें, इस तरह मेमोरी आपके लिए संभाला जाता है।

उदाहरण:

struct MyException : public std::exception
{
   std::string s;
   MyException(std::string ss) : s(ss) {}
   ~MyException() throw () {} // Updated
   const char* what() const throw() { return s.c_str(); }
};

और फिर अपने कोड में इसका उपयोग करें:

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw MyException("it's the end of the world!");
  }
}

void Foo::Caller(){
  try{
    this->Bar();// should throw
  }catch(MyException& caught){
    std::cout<<"Got "<<caught.what()<<std::endl;
  }
}

5
क्या std :: runtime_exception से प्राप्त करना बेहतर नहीं होगा?
मार्टिन यॉर्क

ध्यान दें कि क्रिस्टोफर_फ का तर्क अभी भी मान्य है: आपका अपवाद निर्माण पर एक अपवाद फेंक सकता है ... विचार के लिए भोजन, मुझे लगता है ...: -ड ... मैं गलत हो सकता है, लेकिन अपवाद उनके कब्जे के माध्यम से पकड़ा जाना चाहिए- संदर्भ संख्या?
पीरसेबल

कॉन्स्टेंट-रेफरेंस के लिए यह संभव है, लेकिन अनिवार्य नहीं। मैंने इसके बारे में थोड़ी देर सोचा ... इसके लिए या इसके खिलाफ कोई संदर्भ नहीं मिला।
पियरेबीडीआर

यहाँ कॉन्स्ट रेफरी केवल उपयोगी है ताकि आप गलती से कैच-ब्लॉक में अपवाद को संशोधित न करें। जो आप वैसे भी नहीं करेंगे तो बस नॉनकॉस्ट रेफरी द्वारा पकड़ लें

मैंने इस कोड को आज़माया, एक संकलन त्रुटि थी, विध्वंसक विधि के बारे में कुछ ...
dividebyzero

24

ये सभी काम:

#include <iostream>
using namespace std;

//Good, because manual memory management isn't needed and this uses
//less heap memory (or no heap memory) so this is safer if
//used in a low memory situation
void f() { throw string("foo"); }

//Valid, but avoid manual memory management if there's no reason to use it
void g() { throw new string("foo"); }

//Best.  Just a pointer to a string literal, so no allocation is needed,
//saving on cleanup, and removing a chance for an allocation to fail.
void h() { throw "foo"; }

int main() {
  try { f(); } catch (string s) { cout << s << endl; }
  try { g(); } catch (string* s) { cout << *s << endl; delete s; }
  try { h(); } catch (const char* s) { cout << s << endl; }
  return 0;
}

आपको h to f को g करना पसंद करना चाहिए। ध्यान दें कि कम से कम बेहतर विकल्प में आपको मेमोरी को स्पष्ट रूप से मुक्त करने की आवश्यकता है।


1
लेकिन एक const charसूचक को एक स्थानीय चर बग के लिए फेंकना नहीं है ? हां, निश्चित रूप से मुझे पता है कि कंपाइलर सी-स्ट्रिंग को अनमॉडिफाइड सेक्शन में रखेगा, जो किसी ऐप के चलने तक एड्रेस नहीं बदलेगा। लेकिन यह अपरिभाषित है; इससे अधिक, क्या होगा यदि यह कोड एक पुस्तकालय में था जो केवल एक त्रुटि फेंकने के बाद चला गया था? Btw, मैंने भी अपने प्रोजेक्ट में कई ऐसे बुरे काम किए हैं, मैं सिर्फ एक छात्र हूं। लेकिन मुझे इसके बारे में सोचना चाहिए था ...
हाय-एंजेल

1
@ हाय-एंजेल कोई स्थानीय चर नहीं है; क्या फेंका गया है, एक स्ट्रिंग शाब्दिक है, जिसका जीवनकाल के संदर्भ में मानक द्वारा विशिष्ट और अच्छी तरह से परिभाषित उपचार है, और आपकी चिंताएं मूक हैं। उदाहरण के लिए देखें stackoverflow.com/a/32872550/2757035 अगर यहां कोई समस्या थी, तो मूल रूप से संदेशों को फेंकना कभी भी काम नहीं कर सकता था (कम से कम अनुचित अतिरिक्त कलाबाजी / जोखिम की आवश्यकता के बिना नहीं), तो निश्चित रूप से वहाँ नहीं है, और यह ठीक है ।
अंडरस्कोर_ड

8

यह काम करता है, लेकिन मैं यह नहीं करता अगर मैं तुम थे। जब आप काम कर रहे हों, तो आप उस ढेर डेटा को हटाते नहीं दिखते हैं, जिसका अर्थ है कि आपने मेमोरी लीक बना लिया है। C ++ कंपाइलर यह सुनिश्चित करने का ध्यान रखता है कि स्टैक पॉप अप होने के बावजूद भी अपवाद डेटा को जीवित रखा जाए, इसलिए यह न महसूस करें कि आपको हीप का उपयोग करने की आवश्यकता है।

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


8

संभवतः std :: व्युत्पन्न से कुछ फेंकने के अलावा, आपको अनाम टेंपरेरी फेंकना चाहिए और संदर्भ द्वारा पकड़ना चाहिए:

void Foo::Bar(){
  if(!QueryPerformanceTimer(&m_baz)){
    throw std::string("it's the end of the world!");
  }
}

void Foo:Caller(){
  try{
    this->Bar();// should throw
  }catch(std::string& caught){ // not quite sure the syntax is ok here...
    std::cout<<"Got "<<caught<<std::endl;
  }
}
  • आपको अनाम टेम्परेरी फेंकनी चाहिए, ताकि कंपाइलर आपके द्वारा फेंके जाने वाले वस्तु के जीवनकाल से संबंधित हो - यदि आप ढेर से कुछ नया-एड फेंकते हैं, तो किसी और को चीज़ को खाली करने की आवश्यकता होती है।
  • ऑब्जेक्ट स्लाइसिंग को रोकने के लिए आपको संदर्भों को पकड़ना चाहिए

विवरण के लिए मेयर का "प्रभावी C ++ - 3rd संस्करण" देखें या https://www.securecoding.cert.org/.../ERR02-A.++hrow+anonymous+temporaries+and+catch+by-reference पर जाएं


5

C ++ में अपवाद को फेंकने का सबसे सरल तरीका:

#include <iostream>
using namespace std;
void purturb(){
    throw "Cannot purturb at this time.";
}
int main() {
    try{
        purturb();
    }
    catch(const char* msg){
        cout << "We caught a message: " << msg << endl;
    }
    cout << "done";
    return 0;
}

यह प्रिंट:

We caught a message: Cannot purturb at this time.
done

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

This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.


3
यह एक बुरे विचार की तरह लगता है - catch (std::exception&)इसे पकड़ नहीं पाएंगे।
टिम्मम्म

1

हालाँकि यह प्रश्न पुराना है और पहले से ही उत्तर दे दिया गया है, मैं सिर्फ C ++ 11 में उचित अपवाद हैंडलिंग कैसे करें पर एक नोट जोड़ना चाहता हूं :

का उपयोग करें std::nested_exceptionऔरstd::throw_with_nested

इनका उपयोग करते हुए, मेरी राय में, अपवाद डिज़ाइन को क्लीनर बनाता है और अपवाद वर्ग पदानुक्रम बनाने के लिए अनावश्यक बनाता है।

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

चूंकि आप इसे किसी भी व्युत्पन्न अपवाद वर्ग के साथ कर सकते हैं, इसलिए आप इस तरह के बैकट्रेस में बहुत सारी जानकारी जोड़ सकते हैं! आप GitHub पर मेरे MWE पर एक नज़र डाल सकते हैं , जहां एक बैकट्रेस कुछ इस तरह दिखेगी:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.