2016-11-24 60 views
8

假設我有以下兩個文件,main.cpp編譯器如何知道要使用哪個catch塊?

#include <iostream> 

class A {};  
void foo(); 

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

foo.cpp

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

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

現在,當我單獨編譯每個文件,鏈接生成的目標文件,並運行結果可執行文件,我得到預期的結果:

$ 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. 

而這讓我難以置信!類A沒有虛擬方法。因此,它不是多形的,它的實例在運行時不應攜帶任何類型信息。 main.o目標文件不知道正在拋出什麼,因爲實際拋出發生在foo()內部,其主體在單獨的編譯單元中定義。目標文件具有更多信息,但同樣不瞭解任何捕獲語句和捕獲的異常的預期類型。

簡而言之:我沒有看到兩個源文件分別編譯然後鏈接如何生成上面的輸入,而無需處理一些運行時類型信息。這兩個文件分別編譯應該有足夠的信息來採取正確的catch塊。

+1

B是A的子類,因此您可以在B的任何實例上查看A的「透鏡」。 – freestyle

+0

爲什麼要重新定義「A類」? – Raindrop7

+0

由於協變 – arturx64

回答

5

這當然完全依賴於編譯器。

所有編譯器的約束條件是:

  • 時拋出異常的類型是已知的(無論是在編譯時,或在運行時的情況下你會拋出一個多態對象)。
  • 適用的catch塊(它們可以是幾個),它們的類型取決於執行路徑。

這意味着類型必須在運行時被識別,即使異常對象是非plymorphic。

實現此目的的簡單方法是將指針傳遞給typeinfo對象以及拋出的對象本身。這是GCC使用的方法:看online code,在這裏foo()投擲方便的摘錄:

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

我明白了。我沒有考慮到,僅僅因爲實際的物體沒有攜帶任何類型的信息並不意味着它們不能在投擲的地點被靜態確定並且與物體一起傳遞。 – Witiko

+2

@Witiko這是一個非常有趣的問題。我生成的代碼使'A'多態,認爲它會使用RTTI。但是否:編譯器繼續傳遞其附加的'typeinfo'。這是因爲捕獲代碼無法知道是否拋出了多態對象或非多態對象,因此需要統一的接口。 – Christophe

2

它的RTTI(運行時類型信息)的組合和實現特定的編碼類型數據在嘗試評估哪個catch塊得到什麼時生成比較類型。

但只是比較兩種類型的面值可能不會產生正確的結果(例如對於基類和派生類)。在Windows的情況下,存在特殊的附加擴展類型信息結構(etype_info或類似的東西),其包含層次結構中需要遍歷以確定潛在匹配的所有類(因爲基類可以是限制視圖進入派生類)。如果遍歷一個匹配被發現,catch塊被調用。

補遺

異常處理需要由OS提供特殊的運行時支持,因此出現這種情況的方法是實現定義(如Windows結構化異常處理),只要最終結果滿足標準。

樓上是它的Windows風味的要點。

+0

RTTI如何發揮作用,那麼,如果完整的類型信息與拋出的對象一起傳遞? – Witiko

+0

它提供了[type_info](http://en.cppreference.com/w/cpp/types/type_info),用於比較操作並且集成到異常信息結構中('excpt_info'或者沿着這些行的東西) 。在返回到catch塊時展開堆棧,該結構被攜帶並用於測試catch塊類型(其類型信息記錄在編譯時記錄的另一個結構中,一旦它看到try-catch)。 –

+1

如果您想了解更多關於此主題的信息,您可能需要探索SEH之類的實現方式,它在網絡上已有詳細記錄。一般RTTI也是如此。希望這可以幫助。 –

相關問題