2016-11-24 42 views
8

Załóżmy, że mam następujące dwa pliki, main.cpp:W jaki sposób kompilator wie, który blok catch ma wykonać?

#include <iostream> 

class A {};  
void foo(); 

int main(void) 
{ 
    try { 
     foo(); 
    } 
    catch(const A& e) { 
     std::cout << "Caught an A." << std::endl; 
    } 
    return 0; 
} 

i foo.cpp:

class A {}; 
class B : public A {}; 

void foo() 
{ 
    B b; 
    throw b; 
} 

Teraz, kiedy mogę skompilować każdy z tych plików osobno, połączyć wynikowe pliki obiektów i uruchom otrzymany wykonywalny, otrzymuję oczekiwany wynik:

$ clang++ --std=c++14 -c main.cpp 
$ clang++ --std=c++14 -c foo.cpp 
$ clang++ --std=c++14 main.o foo.o 
$ ./a.out 
Caught an A. 

I to jest straszne dla mojego umysłu! Klasa A nie ma wirtualnych metod. Dlatego nie jest polimorficzny, a jego wystąpienia powinny zawierać informacje o typie w czasie wykonywania. Plik obiektowy main.o jest nieświadomy tego, co jest rzucane, ponieważ faktyczne rzucanie odbywa się wewnątrz foo(), którego treść jest zdefiniowana w oddzielnej jednostce kompilacji. Plik obiektowy foo.o zawiera więcej informacji, ale jest równie nieświadomy jakichkolwiek instrukcji catch i oczekiwanych typów wychwyconych wyjątków.

W skrócie: Nie widzę, w jaki sposób oba pliki źródłowe zostały skompilowane osobno, a następnie połączone mogą wytworzyć powyższe dane wejściowe, nie dysponując informacjami o typie środowiska wykonawczego. Żaden z plików skompilowanych osobno nie powinien mieć wystarczających informacji, aby wziąć odpowiedni blok catch.

+1

B jest podklasą A, dzięki czemu można zobaczyć na każdej instancji B z „soczewki” A. – freestyle

+0

dlaczego jesteś przedefiniowanie 'klasy A'? – Raindrop7

+0

Ponieważ kowariancji – arturx64

Odpowiedz

5

To oczywiście zależy od kompilatora.

Ograniczenia dla wszystkich kompilatorów są:

  • typ wyjątku wiadomo, kiedy rzucasz (albo w czasie kompilacji lub w czasie wykonywania, w przypadku chcesz rzucać polimorficzny obiekt).
  • odpowiednie bloki catch (ich może być kilka) i ich typy zależą od ścieżki wykonania.

Oznacza to, że typ musi zostać rozpoznany w środowisku wykonawczym, nawet jeśli obiekt wyjątku jest niefmorficzny.

Łatwym sposobem osiągnięcia tego jest przekazanie wskaźnika do obiektu typeinfo wraz z samym obiektem. Jest to podejście stosowane przez GCC: patrz online code tu wyciąg z rzucania w foo() dla wygody:

call __cxa_allocate_exception 
    mov  edx, 0 
    mov  esi, OFFSET FLAT:typeinfo for B ; <== !! 
    mov  rdi, rax 
    call __cxa_throw 
+0

Rozumiem. Nie uznałem, że faktyczny obiekt nie zawiera żadnej informacji o typie, nie oznacza, że ​​nie można go ustalić statycznie w miejscu rzucania i przekazywania wraz z obiektem. – Witiko

+2

@ Witiko To było naprawdę ciekawe pytanie. Wygenerowałem kod powodujący polimorfizm "A", sądząc, że będzie on wtedy korzystał z RTTI. Ale nie: kompilator kontynuuje przekazywanie swojego dodatkowego "typeinfo". Dzieje się tak dlatego, że kod przechwytywania nie może wiedzieć, czy obiekt polimorficzny, czy obiekt nie polimorficzny, jest generowany, a zatem wymagany jest jednolity interfejs. – Christophe

2

Jest to kombinacja RTTI (rtti) oraz realizacji określonych danych kodowany typu, które jest generowany do porównywania typów przy próbie oceny, który blok catch dostaje co.

Jednak porównanie dwóch typów w wartości nominalnej może nie dać prawidłowego wyniku (np. W przypadku klas bazowych i pochodnych). W przypadku systemu Windows & SEH istnieje specjalna dodatkowa struktura informacji rozszerzonego typu (etype_info lub coś w tym rodzaju), która zawiera wszystkie klasy w hierarchii, które muszą zostać przetestowane, aby określić potencjalne dopasowanie (ponieważ klasa podstawowa może być widokiem ograniczającym) do klasy pochodnej). Jeśli po przeszukaniu dopasowania zostanie znalezione, wywoływany jest blok catch.

Uzupełnienie

Obsługa wyjątków wymaga szczególnego wsparcia wykonania dostarczone przez system operacyjny, a więc sposób to się dzieje jest realizacja zdefiniowane (jak Structured Exception Handling w systemie Windows), o ile wynik końcowy spełnia normę.

górze jest Windows smaku Istotą tego.

+0

W jaki sposób RTTI wchodzi w grę, jeśli kompletne informacje o typie są przekazywane wraz z wyrzucanym obiektem? – Witiko

+0

Udostępnia [typ_info] (http://en.cppreference.com/w/cpp/types/type_info), który jest używany w operacjach porównania i jest zintegrowany ze strukturą informacji o wyjątkach ('excpt_info' lub coś w tym stylu) . Odwijania stosu idąc z powrotem do bloku catch, że struktura jest prowadzone i wykorzystywane do testowania przeciwko typu bloku catch (którego rodzaj informacji jest rejestrowana w jeszcze innej strukturze nagranego w czasie kompilacji raz widzi try-catch). –

+1

Jeśli chcesz wiedzieć więcej na ten temat, warto wybrać się do jednego z wdrożeń jak SEH, jest dobrze udokumentowane w internecie. RTTI w ogóle. Mam nadzieję że to pomoże. –