在 Ada 中从基础 class 调用重写的方法
Calling an overridden method from the base class in Ada
我想知道如何从 ADA 中的 parent class 调用覆盖的方法。让我们考虑以下示例。 Parent
class 有一些方法被 Child
class 覆盖。 Parent
class(即Prints
)中有一个方法调用了它的一些重写方法。但是重写的方法没有被调用!这是示例:
--- parent ---
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;
--- child ---
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 如何对待它们?
来自Prints
的调用Self.Print1;
是否错误?
在 Ada 中,仅当对象属于 class-wide 类型时才会进行分派。相关的手册部分是 ARM 3.9.2.
在Parents.Prints
中,控制操作数Self
的类型是Parent
,并且是“静态标记”,所以没有调度。
一种方法是使用“重新调度”,如下所示:
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
的对象是 "dynamically tagged" 并且调用调度。
如您所见,Prints
可以在派生类型中被覆盖。这并不总是(或什至通常?)您想要的。如果不是,将其参数更改为 class-wide:
是合适的
procedure Prints(Self: in out Parent'Class);
(当然还有体内!)然后一切如您所愿。
[旁注:我现在了解到 object.operation 表示法适用于 class 范围的对象!]
思考这个问题的一种方法是从低层次思考它,即编译器生成什么代码。
编译Prints
过程时,看到语句Self.Print1
,编译器生成的代码是非分派调用。这意味着编译器计算出 Print1
方法的地址(或外部符号),并生成对它的调用。这不是间接调用,而是对固定地址的调用,即Parents
中出现的Print1
。它不分派的原因是 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)
),那么调用将是 dispatching,这意味着它基本上是一个间接调用。该代码将在 运行 时间计算出正确过程的地址,并间接调用它。
Ada 和 C++ 的不同之处在于,Ada 编译器使用它正在操作的对象的 类型 来确定是进行调度(间接)还是非调度(称呼)。如果类型是类范围的,则它是调度的,否则它是固定的。但是,C++ 使用方法的 属性 而不是类型的 属性 来决定进行哪种调用;如果该方法被标记为 virtual
,则对它的调用正在调度,如果没有,则它们是固定的。 (至少我认为是这样;我不是真正的 C++ 专家。)
顺便说一句,即使您不使用 Object.Operation 符号,这同样适用。如果您说的不是 Self.Print1
,而是
Print1 (Self);
那将是一个非调度呼叫;但是
Print1 (Parent'Class (Self));
是调度呼叫。
我想知道如何从 ADA 中的 parent class 调用覆盖的方法。让我们考虑以下示例。 Parent
class 有一些方法被 Child
class 覆盖。 Parent
class(即Prints
)中有一个方法调用了它的一些重写方法。但是重写的方法没有被调用!这是示例:
--- parent ---
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;
--- child ---
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 如何对待它们?
来自Prints
的调用Self.Print1;
是否错误?
在 Ada 中,仅当对象属于 class-wide 类型时才会进行分派。相关的手册部分是 ARM 3.9.2.
在Parents.Prints
中,控制操作数Self
的类型是Parent
,并且是“静态标记”,所以没有调度。
一种方法是使用“重新调度”,如下所示:
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
的对象是 "dynamically tagged" 并且调用调度。
如您所见,Prints
可以在派生类型中被覆盖。这并不总是(或什至通常?)您想要的。如果不是,将其参数更改为 class-wide:
procedure Prints(Self: in out Parent'Class);
(当然还有体内!)然后一切如您所愿。
[旁注:我现在了解到 object.operation 表示法适用于 class 范围的对象!]
思考这个问题的一种方法是从低层次思考它,即编译器生成什么代码。
编译Prints
过程时,看到语句Self.Print1
,编译器生成的代码是非分派调用。这意味着编译器计算出 Print1
方法的地址(或外部符号),并生成对它的调用。这不是间接调用,而是对固定地址的调用,即Parents
中出现的Print1
。它不分派的原因是 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)
),那么调用将是 dispatching,这意味着它基本上是一个间接调用。该代码将在 运行 时间计算出正确过程的地址,并间接调用它。
Ada 和 C++ 的不同之处在于,Ada 编译器使用它正在操作的对象的 类型 来确定是进行调度(间接)还是非调度(称呼)。如果类型是类范围的,则它是调度的,否则它是固定的。但是,C++ 使用方法的 属性 而不是类型的 属性 来决定进行哪种调用;如果该方法被标记为 virtual
,则对它的调用正在调度,如果没有,则它们是固定的。 (至少我认为是这样;我不是真正的 C++ 专家。)
顺便说一句,即使您不使用 Object.Operation 符号,这同样适用。如果您说的不是 Self.Print1
,而是
Print1 (Self);
那将是一个非调度呼叫;但是
Print1 (Parent'Class (Self));
是调度呼叫。