[英]Calling an overridden method from a parent class ctor
我嘗試從父類的構造函數中調用重寫方法,並注意到跨語言的不同行為。
C++
- 回聲A.foo()
class A{
public:
A(){foo();}
virtual void foo(){cout<<"A.foo()";}
};
class B : public A{
public:
B(){}
void foo(){cout<<"B.foo()";}
};
int main(){
B *b = new B();
}
Java
- 回聲B.foo()
class A{
public A(){foo();}
public void foo(){System.out.println("A.foo()");}
}
class B extends A{
public void foo(){System.out.println("B.foo()");}
}
class Demo{
public static void main(String args[]){
B b = new B();
}
}
C#
- 回聲B.foo()
class A{
public A(){foo();}
public virtual void foo(){Console.WriteLine("A.foo()");}
}
class B : A{
public override void foo(){Console.WriteLine("B.foo()");}
}
class MainClass
{
public static void Main (string[] args)
{
B b = new B();
}
}
我意識到在C ++對象是從層次結構中最頂層的父對象創建的,所以當構造函數調用重寫方法時,B甚至不存在,因此它調用方法的A'版本。 但是,我不確定為什么我在Java和C#中獲得不同的行為(來自C ++)
在C ++中,正如您所正確指出的那樣,對象的類型為A
直到A
構造函數完成。 該對象實際上在構造期間改變了類型。 這就是使用A
類的vtable的原因,因此調用A::foo()
而不是B::foo()
。
在Java和C#中,即使在構造基類期間,也始終使用最派生類型的vtable(或等效機制)。 所以在這些語言中, B.foo()
被調用。
請注意,通常不建議從構造函數中調用虛方法。 如果你不是很小心,虛擬方法可能會認為對象是完全構造的,即使不是這種情況。 在Java中,每個方法都隱式虛擬,您別無選擇。
雖然我知道您正在進行實驗,但重要的是要注意以下有效Java第2版,第17項:設計和繼承文檔,或者禁止它 :
為了允許繼承,類必須遵守一些限制。 構造函數不得直接或間接調用可覆蓋的方法 。 如果違反此規則,將導致程序失敗。 超類構造函數在子類構造函數之前運行,因此在子類構造函數運行之前將調用子類中的重寫方法。 如果重寫方法依賴於子類構造函數執行的任何初始化,則該方法將不會按預期運行。
這是一個例子來說明:
public class ConstructorCallsOverride {
public static void main(String[] args) {
abstract class Base {
Base() { overrideMe(); }
abstract void overrideMe();
}
class Child extends Base {
final int x;
Child(int x) { this.x = x; }
@Override void overrideMe() {
System.out.println(x);
}
}
new Child(42); // prints "0"
}
}
這里,當Base
構造函數調用overrideMe
, Child
尚未完成初始化final int x
,並且該方法獲取了錯誤的值。 這幾乎肯定會導致錯誤和錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.