2011-02-05 155 views
0

我有一個關於這個問題:C++繼承問題

class A 
{ 
    int a; 
    int* pa; 
public: 
    A(int i):a(i) , pa(new int(a)) 
    { 
     cout<<"A ctor"<<a<<endl; 
    } 
    ~A() 
    { 
     delete pa; 
     cout<<"dtor\n"; 
    } 
    int * &get() 
    { 
    return pa; 
    } 
}; 

class B : public A 
{ 
    int b; 
public: 
     B (A obj): A(obj) , b(0) 
     { 
     cout<<"B ctor\n"; 
     } 
     ~B() 
     { 
     cout<<"B dtor\n"; 
     } 
}; 

int main() 
{ 
int i = 23 ; 
A* p = new B(i); 
} 

能告訴我爲什麼在main編譯的最後一行?我將int傳遞給B的構造函數,該構造函數需要使用A對象。我相信intB的構造函數中被翻譯爲A,但爲什麼?

在此先感謝。

Avri。

回答

5

因爲我們還沒有宣佈A構造函數explicit編譯器創建的A的anomymous實例使用i,並用它來初始化B實例。如果您不希望編譯器執行這些隱式轉換,請將您的構造函數聲明爲explicit。然後你會得到一個編譯器錯誤。

1

由於沒有從intA轉換,暗示你的代碼轉換成

A* p = new B(A(i)); 
+0

偉大的tnx,所以例如,如果我添加而不是int其他類型,如char *行不會編譯becasue A沒有適當的c'tor來處理上述類型。 – Avri 2011-02-05 10:33:21

2

因爲A有一個參數構造函數需要一個int並沒有標示explicit你可以隱式轉換的intA

當你這樣做new B(i),因爲B唯一可行的構造函數採用A,試圖以i轉換爲A,構建不同於新B。此轉換是通過使用構造函數創建臨時A來完成的,該構造函數採用int

B對象被構造,所述基類是A複製從臨時A這意味着從臨時A複製成員變量apa構成。

嚴格說來,因爲構造函數按值取A對象,所以在概念上,該臨時對象被再次複製。然而,編譯器可以通過直接從i構造B的構造函數參數來消除臨時性,所以效果看起來好像只是一個副本。

這將導致一個嚴重的錯誤,因爲當臨時A被破壞,delete pa將導致動態分配int被摧毀,但新分配的B對象的基類將仍然有這個指針的拷貝現在沒有對一個無效對象的長點。如果編譯器沒有刪除其中一個副本,則會立即發生「雙重釋放」。

A的關鍵方面是它有一個用戶定義的析構函數來執行資源操作(取消分配)。這是一個強烈的警告,A需要用戶定義的複製構造函數和複製賦值運算符,因爲編譯器生成的版本可能不會與A的設計一致。

這被稱爲「三個規則」,它說如果你需要一個析構函數的用戶定義版本,複製構造函數或複製賦值運算符,那麼你很可能需要用戶定義的所有版本他們。

如果您試圖在您的示例中釋放動態分配的B對象,則可能會導致「雙重釋放」錯誤。另外,A的析構函數需要標記爲virtual,通過指向A的指針才能正常工作。