throw nowe std :: wyjątek vs throw std :: wyjątek


113

patrząc na kod, na który natknąłem się:

throw /*-->*/new std::exception ("//...

i zawsze myślałem, że nie potrzebujesz / nie powinieneś newtutaj używać .
Jaki jest właściwy sposób, czy oba są w porządku, a jeśli tak, to czy jest jakaś różnica?

BTW z tego, co widzę podczas "grepping" z PowerShell Boost, biblioteki nigdy nie używają throw new.

PS również znalazłem kod CLI, który używa throw gcnew. Czy to w porządku?


1
Myślę, że throw gcnewprzydałoby się np. jeśli chcesz, aby kod zarządzany przechwytywał Twój wyjątek. Czy ktoś może mnie w tym poprawić?
jpalecek

1
.Net radzi sobie z wyjątkami według wskaźnika, więc rzut gcnew jest tam właściwą rzeczą.
Sebastian Redl

1
@SebastianRedl .Net „wskaźnik” może być niejednoznaczny? Chociaż gcnew z pewnością nie jest. System::Exceptionjest ogólnie odwołaniem do zarządzanego obiektu na stercie zbierania elementów bezużytecznych. Zawsze rzucałem gcnewi łapałem System::Exception ^. Oczywiście finallycały czas używam również w C ++ / CLI, chociaż nie często mieszam się z wyjątkami C ++ w tym samym trybloku, nie jestem pewien dlaczego.

Odpowiedzi:


89

Konwencjonalnym sposobem zgłaszania i przechwytywania wyjątków jest zgłoszenie obiektu wyjątku i przechwycenie go przez odwołanie (zwykle constodwołanie). Język C ++ wymaga, aby kompilator wygenerował odpowiedni kod, aby skonstruować obiekt wyjątku i odpowiednio go wyczyścić w odpowiednim czasie.

Rzucanie wskaźnika do dynamicznie przydzielanego obiektu nigdy nie jest dobrym pomysłem. Wyjątki mają umożliwić pisanie bardziej niezawodnego kodu w obliczu błędów. Jeśli wyrzucisz obiekt wyjątku w konwencjonalny sposób, możesz być pewien, że niezależnie od tego, czy zostanie przechwycony przez klauzulę catch z nazwą poprawnego typu, przez a catch (...), czy zostanie następnie ponownie wyrzucony, czy nie, zostanie poprawnie zniszczony w odpowiednim czasie. (Jedynym wyjątkiem jest sytuacja, w której nigdy nie zostanie złapany, ale jest to sytuacja niemożliwa do odzyskania, niezależnie od tego, jak na to spojrzysz).

Jeśli rzucasz wskaźnik do dynamicznie przydzielanego obiektu, musisz być pewien, że niezależnie od tego, jak wygląda stos wywołań w miejscu, w którym chcesz zgłosić wyjątek, istnieje blok catch, który nazywa właściwy typ wskaźnika i ma odpowiednie deletewywołanie. Twój wyjątek nigdy nie może zostać przechwycony, catch (...)chyba że ten blok ponownie wyrzuci wyjątek, który jest następnie przechwytywany przez inny blok catch, który poprawnie radzi sobie z wyjątkiem.

W rzeczywistości oznacza to, że skorzystałeś z funkcji obsługi wyjątków, która powinna ułatwić pisanie solidnego kodu i bardzo utrudniłaby pisanie kodu, który byłby poprawny we wszystkich sytuacjach. Pomija to problem, że prawie niemożliwe będzie działanie jako kod biblioteki dla kodu klienta, który nie będzie oczekiwał tej funkcji.


1
„Rzuć obiekt wyjątku” na stos lub stertę przyjacielu? Stos czy sterta? (Może gdzieś patrzyłem na zły globalny przykład) a jeśli stos, to jaki jest odpowiedni zakres?

@ebyrob: Nie jestem pewien, o co pytasz, ale wygląda na to, że chcesz wiedzieć o przechowywaniu i / lub czasie życia obiektu wyjątku, na który można tutaj znaleźć odpowiedź . Jeśli nie, lepiej zadaj oddzielne pytanie.
CB Bailey

31

Nie ma potrzeby używania newpodczas zgłaszania wyjątku.

Tylko napisz:

throw yourexception(yourmessage);

i złap jako:

catch(yourexception const & e)
{
      //your code (probably logging related code)
}

Zauważ, że yourexceptionpowinno pochodzić std::exceptionbezpośrednio lub pośrednio.


7
Czemu? dlaczego nie używać new? dlaczego pochodzić yourexceptionz std::exception?
Walter,

Kiedy jestem leniwy (co często się zdarza), dlaczego nie throw std::exception;działa? g ++ nie wydaje się go skompilować ...

7
@ebyrob: std::exceptionjest typem i nie możesz rzucić typu , musisz rzucić obiekt . Więc składnia powinna wyglądać następująco: throw std::exception();To się skompiluje. Jak to dobrze, to zupełnie inna kwestia.
Nawaz

22

Zgłaszanie new std::exceptionjest poprawne, jeśli strona wywołująca spodziewa się złapać std::exception*. Ale nikt nie będzie oczekiwał, że złapie wskaźnik do wyjątku. Nawet jeśli udokumentujesz to, co robi twoja funkcja, a ludzie przeczytają dokumentację, nadal mogą zapomnieć i std::exceptionzamiast tego próbują złapać odniesienie do obiektu.


27
Zgłaszanie new std::exceptionjest poprawne tylko wtedy, gdy witryna wywołania oczekuje przechwycenia wskaźnika ORAZ oczekuje przejęcia zarządzania wyjątkiem alokacji ORAZ nigdy nie będzie przypadków, w których funkcja zostanie wywołana przez coś, co nie jest jawnie przechwytywane prawidłowy wskaźnik ( catch(...)lub brak obsługi), w przeciwnym razie nastąpi wyciek obiektu. Krótko mówiąc, można to w przybliżeniu określić jako „nigdy”.
CB Bailey

Ciekawe, jak ta odpowiedź została przyjęta, skoro tak naprawdę to komentarz @ CharlesBaileya jest poprawną odpowiedzią.
John Dibling,

@John: To też przyszło mi do głowy. Ale myślę, że ten jeden-dwa uderzenie ma dobry efekt, gdy podam suche podsumowanie, a Charles w zabawny sposób rozwija różne sposoby, w jakie ludzie są skłonni zapomnieć, jak sobie z tym poradzić. Szkoda jednak, że nie zyskujesz reputacji dzięki komentarzom, które zostały przegłosowane.

Karol nie udzielił odpowiedzi, a to A (w przeciwieństwie do drugiego) ma wyjaśnienia zarówno w A, jak i komentarzu.
NoSenseEtAl

9

C ++ FAQ zawiera ciekawą dyskusję na ten temat:

  1. https://isocpp.org/wiki/faq/exceptions#what-to-catch
  2. https://isocpp.org/wiki/faq/exceptions#catch-by-ptr-in-mfc

Zasadniczo „chyba, że ​​nie ma dobrego powodu, aby tego nie robić, łapać przez odniesienie. Unikaj łapania według wartości, ponieważ powoduje to utworzenie kopii, a kopia może zachowywać się inaczej niż to, co zostało rzucone. Tylko w bardzo szczególnych okolicznościach należy łapać za pomocą wskaźnika. "


2
Jak zwykle FAQ jest źle sformułowany. Możesz złapać według wartości lub odniesienia. Wskaźnik jest po prostu wartością (którą przechwytujesz według wartości lub odniesienia). Pamiętaj, że typ Aróżni się od typu, A*więc jeśli to zrobię throw A(), NIE mogę go złapać, catch(A* e)ponieważ jest to zupełnie inny typ.
Martin York

Te linki są teraz uszkodzone.
stephenspann

1
Naprawiłem linki @spanndemic
user1202136

1

Operator nowy nie może zagwarantować, że nigdy nie zgłosi wyjątku. Z tego powodu użycie go do wyrzucenia „prawidłowego” (zamierzonego) wyjątku spowodowałoby powstanie kodu, którego nie można zagwarantować, że nie ulegnie awarii. Ponieważ w danym momencie może występować tylko jeden wyjątek, a program próbuje zgłosić dwa, zanim którykolwiek z nich zostanie przechwycony, najlepszą rzeczą, jaką może zrobić implementacja, jest natychmiastowe przerwanie programu, np. Przez wywołanie std :: terminate.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.