2013-10-14 100 views
0

我需要一些特定段落的解釋,我read hereC++多態概念

所以在第一個例子:

// pointers to base class 
#include <iostream> 
using namespace std; 

class CPolygon { 
protected: 
int width, height; 
public: 
void set_values (int a, int b) 
    { width=a; height=b; } 
}; 

class CRectangle: public CPolygon { 
public: 
int area() 
    { return (width * height); } 
}; 

class CTriangle: public CPolygon { 
public: 
int area() 
    { return (width * height/2); } 
}; 

int main() { 
CRectangle rect; 
CTriangle trgl; 
CPolygon * ppoly1 = &rect; 
CPolygon * ppoly2 = &trgl; 
ppoly1->set_values (4,5); 
ppoly2->set_values (4,5); 
cout << rect.area() << endl; 
cout << trgl.area() << endl; 
return 0; 
} 

爲什麼我不能簡單地指ppoly1->area() & ppoly2->area()?由於這兩個指針(即使它們屬於類CPolygon)都指向包含派生類對象的地址。

+0

**因爲語言不工作的方式**編譯器不夠聰明,無法確定在執行時它們將指向定義了'area()'的東西,此外,這不是編譯器的工作。 – Beta

+4

您是否閱讀過本教程的其餘部分,其中解釋了虛擬成員? – andy256

+0

@ andy256我的確看過,但我腦子裏仍然有這個問題。另外,當我可以創建派生類的對象並調用它的區域功能並完成我的工作時,爲什麼我會使用基類的指針指向派生類的成員(通過實現虛函數)? –

回答

7

由於CPolygon中沒有聲明virtual int area();,因此該方法通常不會與類CPolygon的對象關聯。基本上,您希望方法查找在運行時發生(比如它在Python,JavaScript和其他語言中完成),但在C++中它發生在編譯時。上面的代碼含蓄對C++編譯器說,「CRectangleCTriangle有方法名爲area,但它們沒有任何關係,也沒有任何關係與CPolygon。」

請記住,C++編譯器在編譯時通常不會看到給定類的所有子類。


我爲什麼會用基類的指針指向派生類的成員(通過實現虛擬函數)時,我可以創建派生類的一個對象,並調用它的區域功能,並得到我的工作完成了?

因爲代碼知道關於它所使用的對象的每一個細節都很少,而且通常不需要知道,所以就有了簡化。

例如,在這種情況下,如果您的代碼的全部內容是一個函數,它創建了一個CTriangle,然後調用它的area方法來獲取區域,打印並終止,那麼沒錯。代碼只不過是一個具有額外的動機的榮耀的計算器。

但是在現實世界中,您並不具備所有適合一種功能或源文件的奢侈品。所以一段代碼可能知道它有一個指向CPolygon的指針,但沒有指定什麼特定的子類。然而,該代碼需要知道它的區域。例如,計算的擠出容積的函數給出的橫截面多邊形,厚度:

int extruded_volume(CPolygon* poly, int thickness) 
{ 
    return thickness * poly->area(); 
} 

想象這個函數是在Extrusion.h聲明並在Extrusion.cpp定義。此外,假設CPolygon,CRectangleCTriangle中的每一個分別是,.h文件中聲明的文件,並且在.cpp文件中分別定義了文件名。然後Extrusion.h只需要#include "CPolygon.h",並可以不知道任何關於各個子類的事情而生活。 當且僅當area是一種虛擬方法。

這也允許稍後添加CPolygon的子類。例如,代表某一橢圓的「寬度」和「高度」分別爲它的短軸和長軸的長度,一個子類CEllipse

class CEllipse: public CPolygon { 
public: 
int area() 
    { return M_PI * (width/2) * (height/2); } 
}; 

由於我們使用的虛擬方法,一個CEllipse*可以被傳遞到extruded_volume而不改變後者的代碼是

如果area不是虛方法,那麼你就需要的extruded_volume一個單獨的版本爲每個可能的多邊形:

template<typename T> 
int extruded_volume(T* poly, int thickness) 
{ 
    return thickness * poly->area(); 
} 

在這種情況下,代碼膨脹是次要的,因爲沒有太多的代碼在那裏,但在更大的系統中,這可能是不可接受的。還請注意,所有調用extruded_volume的內容都可能需要成爲模板。

+0

當我可以創建派生類的對象並調用它的區域函數並完成我的工作時,爲什麼我會使用基類的指針指向派生類的成員(通過實現虛函數)? –

+0

參見上述附錄。我決定跳過「如果你不能使用模板並且必須執行運行時子類標識」的情況,因爲它已經是tl; dr了。 –

+0

感謝您的詳細解釋。 –

0

因爲繼承只能向上工作。您可以調用在類中定義或在該類父類中定義的任何函數,但不能在派生類中調用方法。

有兩種方法可以解決此

演員:

((*CRectangle)ppoly1)->area() //C style 
static_cast<*CRectangle>(ppoly1)->area //C++ style 

虛擬方法:

class CPolygon { 
public: 
virtual int area() { return (0); } 
... 
+0

實際上,C++風格應該要求您使用'dynamic_cast'而不是'static_cast',並且您必須檢查'dynamic_cast'的結果,因爲如果原始指針事實上並不指向派生類型,它將返回一個空指針(例如它來自「CTriangle」)。 C的方式會引起所有人的注意,並相信你的指針是合法的。使用'static_cast'從派生類到它的基類。另外,'*'出現在類型名稱後面。 –

+0

在某些代碼中,可以找到'dynamic_cast'形式'if(CRectangle * pr = dynamic_cast (ppoly1)){cout << pr-> area()<< endl; } else {cerr <<「不是一個矩形。」 << endl; }'儘管這使我困擾,因爲我不喜歡在'if(...)'裏面看見'=',儘管它在這裏是正確的。 –

+0

@MikeDeSimone在這種情況下,實際上'static_cast'已經足夠了(僅僅是因爲這些類型是已知的並且是靜態分配的,一般來說並不安全)。另外C++中的C風格轉換與reinterpret_cast不同,請參閱[this](http://stackoverflow.com/questions/28002/regular-cast-vs-static-cast-vs-dynamic-cast)作爲參考 – PeterT