[英]Calling an overridden method from the base class in Ada
我想知道如何從ADA中的父類調用重寫方法。 讓我們考慮以下示例。 Parent
類有一些被Child
類重寫的方法。 Parent
類中有一個方法(即Prints
)調用它的一些重寫方法。 但是被覆蓋的方法沒有被調用! 這是一個例子:
---父母---
package Parents is
type Parent is tagged null record;
procedure Prints(Self: in out Parent);
-- these will be overridden
procedure Print1(Self: in out Parent) is null;
procedure Print2(Self: in out Parent) is null;
end Parents;
...
package body Parents is
procedure Prints(Self: in out Parent) is
begin
Put_Line("Parents.Prints: calling prints...");
Self.Print1;
Self.Print2;
end;
end Parents;
---孩子---
With Parents;
package Childs is
type Child is new Parents.Parent with null record;
overriding procedure Print1(Self: in out Child);
overriding procedure Print2(Self: in out Child);
end Childs;
...
package body Childs is
procedure Print1(Self: in out Child) is
begin
Put_Line("Child.Print1 is printing...");
end;
procedure Print2(Self: in out Child) is
begin
Put_Line("Child.Print2 is printing...");
end;
end Childs;
---主要---
procedure Main is
anyprint : access Parents.Parent'Class;
begin
anyprint := new Childs.Child;
anyprint.Prints;
end Main;
問題
我期待看到的是從Child
調用Print1
和Print2
。 但是被覆蓋的方法沒有被調用! 擁有C ++背景,這種類型的多態調用對我來說很有意義,但我無法弄清楚Ada如何對待它們?
是調用Self.Print1;
來自Prints
錯了嗎?
在Ada中,只有在對象屬於類范圍類型時才會進行調度。 相關的手冊部分是ARM 3.9.2 。
在Parents.Prints
,控制操作數Self
是Parent
類型,並且是“靜態標記”,因此沒有調度。
一種方法是使用“redispatching”,如下所示:
procedure Prints(Self: in out Parent) is
begin
Put_Line("Parents.Prints: calling prints...");
Parent'Class (Self).Print1;
Parent'Class (Self).Print2;
end;
其中視圖轉換Parent'Class (Self)
表示調用.Print1
的對象是“動態標記”並且調用調度。
如您Prints
, Prints
可以在派生類型中被覆蓋。 這並不總是(甚至通常?)你想要的。 如果不是,則將其參數更改為類范圍是合適的:
procedure Prints(Self: in out Parent'Class);
(當然,在身體里!)然后一切都按照你的預期運作。
[旁注:我現在已經了解到object.operation符號適用於類范圍的對象!]
考慮這個問題的一種方法是在較低級別考慮它,即編譯器生成什么代碼。
編譯Prints
過程並看到語句Self.Print1
,編譯器生成的代碼是非調度調用。 這意味着編譯器計算出Print1
方法的地址(或外部符號),並生成對它的調用。 這不是一個間接調用,而是一個固定的地址,電話,這將是Print1
出現在Parents
。 它不發送的原因是Self
的類型不是類范圍類型(它只是Parent
,而不是Parent'Class
)。
聲明Child
類型時,它將繼承Prints
過程。 也就是說,有一個隱式聲明的過程,如下所示:
procedure Prints (Self : in out Child); -- inherited procedure
但是,如果不覆蓋它,編譯器不會為此隱式過程生成新代碼。 因此,當調用Prints
時,即使使用Child
參數調用它,代碼也會像它是Parent
參數一樣。 並且,如前一段所述,代碼對Print1
和Print2
進行固定調用,而不是調度(間接)調用,這仍然是在Parent
聲明的,因為代碼是在編譯Parent
時生成的。
回到Prints
的調用:
Self.Print1;
如果Self的類型是Parent'Class
,或者如果您使用視圖轉換將其轉換為Parent'Class
,如Simon的答案( Parent'Class(Self)
),那么調用將是調度 ,這意味着它基本上是間接的呼叫。 代碼將在運行時計算出正確過程的地址,並對其進行間接調用。
Ada和C ++之間的區別在於Ada編譯器使用它所操作的對象的類型來確定是進行調度(間接)還是非調度(調用)。 如果類型是classwide,它是調度,否則它是固定的。 但是,C ++使用方法的屬性而不是類型的屬性來決定要進行哪種調用; 如果方法標記為virtual
,則調用它是調度,如果不是,則修復它們。 (至少我認為是這樣的;我不是真正的C ++專家。)
順便說一句,即使您不使用Object.Operation表示法也是如此。 如果,而不是Self.Print1
,你說
Print1 (Self);
這將是一個非調度電話; 但
Print1 (Parent'Class (Self));
是一個調度電話。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.