簡體   English   中英

從Ada中的基類調用重寫方法

[英]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調用Print1Print2 但是被覆蓋的方法沒有被調用! 擁有C ++背景,這種類型的多態調用對我來說很有意義,但我無法弄清楚Ada如何對待它們?

是調用Self.Print1; 來自Prints錯了嗎?

在Ada中,只有在對象屬於類范圍類型時才會進行調度。 相關的手冊部分是ARM 3.9.2

Parents.Prints ,控制操作數SelfParent類型,並且是“靜態標記”,因此沒有調度。

一種方法是使用“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的對象是“動態標記”並且調用調度。

如您PrintsPrints可以在派生類型中被覆蓋。 這並不總是(甚至通常?)你想要的。 如果不是,則將其參數更改為類范圍是合適的:

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參數一樣。 並且,如前一段所述,代碼對Print1Print2進行固定調用,而不是調度(間接)調用,這仍然是在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM