Chciałbym zgłosić wyjątek, gdy moje metody C ++ napotykają coś dziwnego i nie mogą odzyskać. Czy można rzucać std::string
wskaźnikiem?
Oto, czego nie mogłem się doczekać:
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;
}
}
Odpowiedzi:
Tak.
std::exception
jest podstawową klasą wyjątków w standardowej bibliotece C ++. Możesz chcieć uniknąć używania ciągów jako klas wyjątków, ponieważ one same mogą zgłosić wyjątek podczas użycia. Jeśli tak się stanie, to gdzie będziesz?boost ma doskonały dokument na temat dobrego stylu wyjątków i obsługi błędów. Warto przeczytać.
źródło
Kilka zasad:
masz klasę bazową std :: wyjątek, powinieneś mieć swoje wyjątki z niej wywodzące. W ten sposób ogólny program obsługi wyjątków nadal ma pewne informacje.
Nie rzucaj wskaźników, ale sprzeciwiaj się, w ten sposób pamięć jest obsługiwana za Ciebie.
Przykład:
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(); } };
A potem użyj go w swoim kodzie:
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; } }
źródło
Wszystkie te prace:
#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; }
Wolisz h od f do g. Zauważ, że w najmniej korzystnej opcji musisz wyraźnie zwolnić pamięć.
źródło
const char
wskaźnika do zmiennej lokalnej nie jest błędem? Tak, oczywiście wiem, że kompilator umieści łańcuch c w niezmodyfikowanej sekcji, która nie zmieni adresu do czasu uruchomienia aplikacji. Ale to jest nieokreślone; co więcej, co by było, gdyby ten kod znajdował się w bibliotece, która zniknęła tuż po rzuceniu błędu? Przy okazji, ja też zrobiłem wiele takich złych rzeczy w swoim projekcie, jestem tylko studentem. Ale powinienem był o tym pomyśleć ...Działa, ale nie zrobiłbym tego na twoim miejscu. Wydaje się, że po zakończeniu nie usuwasz tych danych sterty, co oznacza, że utworzyłeś wyciek pamięci. Kompilator C ++ dba o to, aby dane wyjątków były utrzymywane przy życiu, nawet gdy stos jest zdejmowany, więc nie myśl, że musisz używać sterty.
Nawiasem mówiąc, rzucanie
std::string
nie jest najlepszym podejściem na początek. Będziesz mieć dużo większą elastyczność w przyszłości, jeśli użyjesz prostego obiektu opakowania. Może na razie zawierać tylko znak astring
, ale być może w przyszłości będziesz chciał dołączyć inne informacje, takie jak niektóre dane, które spowodowały wyjątek lub może numer linii (bardzo często). Nie chcesz zmieniać całej obsługi wyjątków w każdym miejscu w bazie kodu, więc idź teraz drogą i nie wyrzucaj surowych obiektów.źródło
Oprócz prawdopodobnie wyrzucenia czegoś pochodzącego z std ::ception, powinieneś rzucić anonimowe tymczasowe i złapać przez odniesienie:
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; } }
.
Aby uzyskać szczegółowe informacje, zobacz „Effective C ++ - 3rd edition” Meyera lub odwiedź https://www.securecoding.cert.org/.../ERR02-A.+Throw+anonymous+temporaries+and+catch+by+reference
źródło
Najprostszy sposób na zgłoszenie wyjątku w 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; }
To drukuje:
We caught a message: Cannot purturb at this time. done
Jeśli złapiesz zgłoszony wyjątek, wyjątek zostanie zawarty i program będzie kontynuowany. Jeśli nie złapiesz wyjątku, program istnieje i wyświetla:
This application has requested the Runtime to terminate it in an unusual way. Please contact the application's support team for more information.
źródło
catch (std::exception&)
nie złapie go.Chociaż to pytanie jest dość stare i zostało już udzielone, chcę tylko dodać uwagę, jak prawidłowo obsługiwać wyjątki w C ++ 11 :
Użyj
std::nested_exception
istd::throw_with_nested
Moim zdaniem ich użycie prowadzi do czystszego projektowania wyjątków i sprawia, że nie jest konieczne tworzenie hierarchii klas wyjątków.
Zwróć uwagę, że umożliwia to uzyskanie śledzenia wstecznego wyjątków w kodzie bez potrzeby debugera lub kłopotliwego rejestrowania. Opisano w StackOverflow tutaj i tutaj , jak napisać odpowiednią procedurę obsługi wyjątków, która będzie ponownie zgłaszać zagnieżdżone wyjątki.
Ponieważ możesz to zrobić z każdą pochodną klasą wyjątków, możesz dodać wiele informacji do takiego śledzenia wstecznego! Możesz również rzucić okiem na moje MWE na GitHubie , gdzie ślad cofania wyglądałby mniej więcej tak:
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"
źródło