2013-04-03 142 views
19
#include <iostream> 
#include <stdio.h> 
using namespace std; 

// Base class 
class Shape 
{ 
    public: 
     void setWidth(int w) 
     { 
     width = w; 
     } 
     void setHeight(int h) 
     { 
     height = h; 
     } 
     Shape() 
     { 
    printf("creating shape \n"); 
     } 
     Shape(int h,int w) 
     { 
    height = h; 
     width = w; 
     printf("creatig shape with attributes\n"); 
     } 
    protected: 
     int width; 
     int height; 
}; 

// Derived class 
class Rectangle: public Shape 
{ 
    public: 
     int getArea() 
     { 
     return (width * height); 
     } 
     Rectangle() 
     { 
    printf("creating rectangle \n"); 
     } 
     Rectangle(int h,int w) 
     { 
    printf("creating rectangle with attributes \n"); 
    height = h; 
     width = w; 
     } 
}; 

int main(void) 
{ 
    Rectangle Rect; 

    Rect.setWidth(5); 
    Rect.setHeight(7); 

    Rectangle *square = new Rectangle(5,5); 
    // Print the area of the object. 
    cout << "Total area: " << Rect.getArea() << endl; 

    return 0; 
} 

的程序的輸出下面C++調用基類構造

creating shape 
creating rectangle 
creating shape 
creating rectangle with attributes 
Total area: 35 

給出當構造這兩個派生類對象我看到的它總是默認基類的構造即called.Is有這個理由嗎?這就是爲什麼python等語言堅持顯式調用基類構造函數而不是隱式調用像C++的原因嗎?

+2

繼承層次結構中的每個構造函數都按照Base - > Derived的順序被調用。析構函數按相反的順序調用。 –

+0

我的問題是「它總是被調用的基類的默認構造函數嗎?」 – liv2hak

+0

@ liv2hak但在我看來,在第二個Rectangle初始化函數調用了Rectangle(int h,int w)構造函數... – Kupto

回答

47

對此的簡短回答是「因爲這就是C++標準指定的內容」。

請注意,您可以隨時指定一個構造函數,是不同於默認,像這樣:

class Shape { 

    Shape() {...} //default constructor 
    Shape(int h, int w) {....} //some custom constructor 


}; 

class Rectangle : public Shape { 
    Rectangle(int h, int w) : Shape(h, w) {...} //you can specify which base class constructor to call 

} 

只有當你不指定調用哪一個基類的默認構造函數被調用。

4

當構造對象時,總是首先構造基類子對象,因此,首先調用基類構造函數,然後調用派生類構造函數。原因是派生類對象包含從基類繼承的子對象。您總是需要調用基類構造函數來初始化基類子對象。我們通常在派生類的成員初始化列表上調用基類構造函數。如果不顯式調用基類構造函數,那麼編譯將調用基類的默認構造函數來初始化基類子對象。但是,默認構造函數的隱式調用並不一定需要工作(例如,如果基類定義了一個無參數無法調用的構造函數)。

當對象超出範圍時,它將首先調用派生類的析構函數,然後調用基類的析構函數。

+0

你是什麼意思?「這不會總是有效的?」 – liv2hak

+1

@ liv2hak如果你的基類定義了一個可以在沒有參數的情況下調用的構造函數,在這種情況下,編譯器不會爲你生成一個默認構造函數,因此,編譯器將無法爲你初始化基類子對象,導致錯誤。所以它在任何時候都不起作用。 – taocp

11

除非您明確調用派生類中的另一個構造函數,否則將調用默認類構造函數。該語言指定了這一點。

Rectangle(int h,int w): 
    Shape(h,w) 
    {...} 

將調用其他基類的構造函數。

0

試想像這樣:當你的子類繼承超類的屬性時,它們不會神奇地出現。你仍然需要構造這個對象。所以,你調用基礎構造函數。想象一下,如果你的類繼承了一個變量,那麼你的超類構造函數將初始化爲一個重要的值。如果我們沒有這樣做,你的代碼可能會失敗,因爲變量沒有被初始化。

1

在C++中,編譯器始終確保對象層次結構中的函數被成功調用。這些函數是構造函數和析構函數,對象層次意味着繼承樹。

根據這條規則,我們可以猜測編譯器會爲繼承層次中的每個對象調用構造函數和析構函數,即使我們沒有實現它。爲了執行此操作,編譯器將爲我們合成未定義的構造函數和析構函數,並將它們命名爲默認構造函數和析構函數。然後,編譯器將調用基類的默認構造函數,然後調用派生類的構造函數。

在你的情況下,你不調用基類構造函數,但編譯器通過調用基類的默認構造函數來爲你做這件事,因爲如果編譯器沒有這樣做,你的例子中的矩形的派生類將不完整,它可能會導致災難,因爲您可能會在派生類中使用基類的某個成員函數。所以爲了安全起見,編譯器總是需要所有的構造函數調用。

0

爲什麼調用基類的默認構造函數?結果並非總是如此。可以從派生類的構造函數調用基類的任何構造函數(具有不同的簽名)。在你的情況下,默認的構造函數被調用,因爲它沒有參數,所以它是默認的。

當派生類被創建時,構造函數被調用的順序在層次結構中總是爲Base - > Derived。如果我們有:

class A {..} 
class B : A {...} 
class C : B {...} 
C c; 

當c是創造,對於A構造函數首先被調用,然後B中的構造函數,然後C.

構造爲了保證秩序,當派生類的構造函數被調用,它總是在派生類的構造函數可以做任何事情之前調用基類的構造函數。因此,程序員可以在派生類的構造函數的唯一初始化列表中手動調用基類的構造函數,並使用相應的參數。例如,在以下代碼中,Derived的默認構造函數將調用Base的構造函數Base :: Base(int i),而不是默認的構造函數。

Derived() : Base(5) 
{  
} 

如果有在派生類的初始化列表來調用函數沒有這樣的構造函數的構造函數,則程序假定一個基類的無參數的構造函數。這就是爲什麼沒有參數的構造函數(即默認構造函數)被調用的原因。