[英]Polymorphism in C++ vs Java
我正在將一些Java代碼轉換為C ++,並且希望保持類結構相似。 但是,我遇到了以下問題,我不知道該如何解決。 我在Java中執行此操作:
public class Mother {
protected Father make;
public Mother(){
make = maker();
make.print(); };
public Father maker(){
return new Father();};}
public class Daughter extends Mother {
public Daughter(){
super();}
@Override
public Father maker(){
return new Son();};}
public class Father {
public void print(){
System.out.println("I am the Father!\n");}}
public class Son extends Father {
@Override
public void print(){
System.out.println("I am the son!\n");};}
public static void main(String[] args) {
Daughter dot = new Daughter();
}
會產生: 我是兒子! 而:
class father{
public:
virtual void print(){
std::cout << "I am the father!\n";
}; };
class son: public father{
public:
virtual void print(){
std::cout << "I am the son!\n";
};};
class mother{
protected:
father *make;
public:
mother(){
make = maker();
make->print();
};
virtual father *maker(){
return new father();
};};
class daughter: public mother{
public:
daughter(): mother() {
};
virtual father *maker(){
return new son();
};};
int main(int argc, const char * argv[]) {
daughter *d = new daughter();
會產生我是父親! 。 如何使C ++代碼產生與Java代碼相同的結果? 謝謝。
Daughter
的構造函數調用Mother
構造函數, Mother
構造函數調用maker()
。 至少在C ++中,由於Daughter
構造不完整,因此在這一點上僅將該對象視為Mother
。 因此,調用了Mother::maker()
,所以這是在做正確的事情。 但是,出於這些原因,通常在構造過程中調用虛擬函數被認為是強烈的代碼味道。
在Java中,顯然,即使在構造過程中,始終會調用子類重寫,因此,Java中的構造函數絕不應調用可重寫方法。 這樣做可能導致不確定的行為。 還有就是這是一個非常不錯的解釋在這里 。
在Java中使用AFAIK時,在構造函數中調用(非最終)方法(這是可以在派生類中重寫的方法)是唯一不好的樣式。 C ++始終會調用實際類的版本,而不是被覆蓋的版本。
您可以通過將對象傳遞給構造函數來解決此問題嗎?
您不應該從基類構造函數中調用虛函數-派生類的vtable尚未鏈接到,因此您將始終結束調用基類的函數。 您也不應該在Java中真正執行此操作,因為它將調用正確的函數,但尚未實例化最多派生的類-這可能導致未定義的行為。 最終,由於不同的原因,兩種語言都是錯誤的。
解決此問題的一種方法是讓派生類將可能虛擬調用的結果傳遞給基類:
daughter(): mother(new son) { }
以便:
mother() : make(new father) { make->print(); }
mother(father * m) : make(m) { make->print(); }
通過委派構造函數,這變得更加容易:
mother()
: mother(new father)
{ }
mother(father* m)
: make(m)
{
make->print();
}
在C ++中,從基本構造函數調用虛擬函數不會調用更多派生的實現。 原因是,對於BASE
類型的構造函數,類型是BASE
,即使從派生類DERIVED
調用了該構造函數。 因此,虛擬函數表仍在構建中,直到DERIVED
構造函數完成執行后,才指向更派生的實現。
Java(和C#)與C ++的不同之處在於,您可以從基本構造函數中調用虛擬函數,並且它將調用派生程度最高的實現。 但是,由於派生程度最高的構造函數尚未運行,因此該對象可能處於未定義狀態,這就是為什么不建議從構造函數中調用虛函數的原因。
關於如何解決它,您可以添加一個initialize
方法,該方法在創建實例后調用。 由於此時將完全構建對象,因此它將調用正確的虛函數(所有語言)。
創建派生對象時,它首先調用Base的構造函數。 在執行Base的構造函數(Mother)時,此對象還不是Derived(daughter)類型; 它的類型仍然僅僅是Base(母親)。有關詳細信息, 請閱讀此文章: http : //www.parashift.com/c%2B%2B-faq-lite/calling-virtuals-from-ctors.html
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.