2009-09-26 10 views
19

Wyobraźmy sobie dwa podobne kawałki kodu:Jaka jest różnica między rzutem a rzutem z argem złapanego wyjątku?

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw err; 
} 

i

try { 
    [...] 
} catch (myErr &err) { 
    err.append("More info added to error..."); 
    throw; 
} 

Są one skutecznie takie same lub różnią się one w jakiś subtelny sposób? Na przykład, czy pierwsza powoduje uruchomienie konstruktora kopiowania, podczas gdy druga może ponownie użyć tego samego obiektu, aby go ponownie rzucić?

Odpowiedz

26

W zależności od tego, jak zostały ułożone hierarchii wyjątku, ponownie rzuca wyjątek nazywając zmienną wyjątku w instrukcji throw może plaster oryginalny obiekt wyjątku.

Rzut ekspresja bez argumentem rzuci aktualny obiekt wyjątku zachowaniu jego dynamicznego typu, podczas gdy ekspresja rzut z argumentem rzuci nowy wyjątek oparty na statycznej typu argumentu throw.

E.g.

int main() 
{ 
    try 
    { 
     try 
     { 
      throw Derived(); 
     } 
     catch (Base& b) 
     { 
      std::cout << "Caught a reference to base\n"; 
      b.print(std::cout); 
      throw b; 
     } 
    } 
    catch (Base& b) 
    { 
     std::cout << "Caught a reference to base\n"; 
     b.print(std::cout); 
    } 

    return 0; 
} 

Jak napisano powyżej, program wyświetli:

Caught a reference to base 
Derived 
Caught a reference to base 
Base

Jeśli throw b jest wymienić na throw, a następnie zewnętrzna połów będzie również złapać pierwotnie rzucone Derived wyjątek. To nadal zachowuje, jeśli klasa wewnętrzna przechwytuje wyjątek o wartości, a nie odwołanie - chociaż oczywiście oznaczałoby to, że oryginalny obiekt wyjątku nie może być modyfikowany, więc wszelkie zmiany w b nie będą odzwierciedlane w wyjątku Derived przechwyconym przez blok zewnętrzny .

+1

Ah, zupełnie zapomniałem o krojeniu! Cholera, to ważne! Dziękuję za to. +1 (Chociaż myślę, że kiedy napisałeś "... zachowując oryginalny typ statyczny ...", miałeś na myśli typ _dynamiczny.To się nazywa _dynamiczny typ_, jeśli nie _ _ oryginalny typ statyczny "_.) - – sbi

+1

Świetne odpowiedź, zupełnie o tym też zapomniałem. – GManNickG

+0

Cieszę się, że ktoś inny wpadł na problem _slicing_;) –

16

W tym drugim przypadku, zgodnie z normą C++ 15,1/6 kopii konstruktora nie jest używany:

Rzut ekspresja bez argumentu ponownie generuje wyjątkiem obrabianych. Wyjątek jest reaktywowany z istniejącym tymczasowym; nie utworzono nowego tymczasowego obiektu wyjątku. Wyjątek nie jest już uważany za przyłapany; dlatego wartość uncaught_exception() znów będzie prawdziwa.

W pierwszym przypadku nowego wyjątku zostanie wyrzucony według 15,1/3:

Rzut ekspresja inicjuje tymczasowego obiektu, zwany obiekt wyjątku, z których typ jest określony przez usunięcie wszelkich kwalifikatory cv najwyższego poziomu ze statycznego typu operandu wyrzucania i dostosowywania typu z "tablicy T" lub "funkcji zwracającej T" do "wskaźnika do T" lub "wskaźnika do funkcji zwracającej T", odpowiednio . < ...> Tymczasowy służy do zainicjowania zmiennej podanej w dopasowującym module obsługi (15.3). Typ wyrażenia throw nie jest niekompletnym typem lub wskaźnikiem lub odniesieniem do niekompletnego typu, innego niż void *, const void *, volatile void *, lub const volatile void *. Z wyjątkiem tych ograniczeń i ograniczeń dopasowania typów, o których mowa w 15.3, argument rzutu jest traktowany dokładnie tak samo jak argument funkcji w wywołaniu (5.2.2) lub operand instrukcji return.

W obu przypadkach skopiować konstruktor jest wymagane na etapie rzutów (15,1/5):

Kiedy rzucony obiekt jest obiektem klasy, a konstruktor kopia używany do inicjacji tymczasowej kopii nie jest dostępny program jest źle sformułowany (nawet wtedy, gdy obiekt tymczasowy mógłby zostać usunięty w inny sposób). Podobnie, jeśli destruktor dla tego obiektu nie jest dostępny, program jest źle sformułowany (nawet wtedy, gdy obiekt tymczasowy mógłby zostać w inny sposób wyeliminowany).

+0

Tak czy inaczej, skoro kopia ctor musi być dostępna dla pierwotnie zgłoszonego wyjątku, nie sądzę, żeby to był problem w tym przypadku. Ale twoja wciąż jest dobrą odpowiedzią. +1 – sbi

+0

Tak, ale w pierwszym przypadku kopia c-tor będzie używana dwukrotnie. –

+1

Mówi, gdy deklaracja określa typ klasy. Jednak w jego przypadku określa typ odniesienia :) Odwołanie zostanie powiązane bezpośrednio i odwoła się do obiektu wyjątku. Tak więc, będziemy wymagać egzemplarza kopii tylko w momencie rzucania, nie łapania w tym przypadku (To by miało znaczenie, gdy na przykład konstruktor kopii bazowej jest chroniony). –

Powiązane problemy