Class 我怎样才能让这个调度电话开始工作?

Class 我怎样才能让这个调度电话开始工作?,class,oop,ada,tag-dispatching,Class,Oop,Ada,Tag Dispatching,我试图熟悉Ada中的面向对象。几个月前,你的网站帮助我解决了另一个O-O问题,我希望你愿意再次提供帮助 情况:我有一个抽象类型“token”和两个派生类型“otoken”和“vtoken”。我想把这两个派生类型放在同一个数组中,并让它们正确地分派 我的教科书建议将数组声明为包含指向token'class的指针,这迫使我在整个过程中遍历点。下面是我的程序的精简版本,但它不会编译,因为编译器说我的分派调用“不明确” ------------------------------------------

我试图熟悉Ada中的面向对象。几个月前,你的网站帮助我解决了另一个O-O问题,我希望你愿意再次提供帮助

情况:我有一个抽象类型“token”和两个派生类型“otoken”和“vtoken”。我想把这两个派生类型放在同一个数组中,并让它们正确地分派

我的教科书建议将数组声明为包含指向token'class的指针,这迫使我在整个过程中遍历点。下面是我的程序的精简版本,但它不会编译,因为编译器说我的分派调用“不明确”

---------------------------------------------------------------------------------

--------------------------------------------
-- Tokensamp.ads
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Command_Line; use Ada.Command_Line;
package tokensamp is
    type token is abstract tagged record
    x: integer;
    end record;
    type otoken is new token with record
    y: integer;
    end record;
    type vtoken is new token with record
    z: integer;
    end record;

    type potoken is access otoken;
    type pvtoken is access vtoken;

end tokensamp;
------------------------------------------------------------------------------------------------------
-- Parsesamp.ads:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Command_Line; use Ada.Command_Line;
with tokensamp; 
package parsesamp is
    function rootOfTree( t: tokensamp.pvtoken) return integer;
    function rootOfTree( t: tokensamp.potoken) return integer;  
end parsesamp; 
-------------------------------------------
-- parsesamp.adb:
package body parsesamp is 
    function rootOfTree( t: tokensamp.pvtoken) return integer  is
    begin
       return   t.z * 2;
    end rootOfTree;

    function rootOfTree( t: tokensamp.potoken) return integer is
    begin
        return  t.y * 2;
    end rootOfTree;
    result: integer;
    type tarray is array (1..2) of access tokensamp.token'class ;
    tl: tarray;
begin
    for i in 1..2 loop
    result := rootOfTree(  tl(i) );
    end loop;

end parsesamp;
-------------------------------------------------------------

当我使用GNAT Ada 95编译器编译此文件时,会收到错误消息:

C:\GNAT\2018\bin\ceblang>gnatmake   parsesamp.adb
gcc -c parsesamp.adb
parsesamp.adb:25:27: ambiguous expression (cannot resolve "rootOfTree")
parsesamp.adb:25:27: possible interpretation at parsesamp.ads:9
parsesamp.adb:25:27: possible interpretation at parsesamp.ads:8
gnatmake: "parsesamp.adb" compilation error

换句话说,它无法将这两个函数识别为备用调度调用。如果您能给我一些建议,我将不胜感激,因为我已经在这方面纠缠了好几天。

您的困惑似乎包括包的使用和Ada中调度操作的定义方式。 调度操作必须在定义标记数据类型的同一包中定义,但在定义任何其他类型之前

package Tokens is
   type token is tagged private;
   function Root_Of_Tree(T : Token) return Integer;
   type Token_Access is access all Token'Class;
   type Token_Array is array (Positive range <>) of Token_Access;
private
   type Token is tagged record
      X : Integer := 1;
   end record;
end Tokens;
我使用了子包来定义Otoken和Vtoken类型

package Tokens.OTokens is
   type Otoken is new Token with private;
   function Root_Of_Tree(T : Otoken) return Integer;
private
   type Otoken is new Token with record
      Y : Integer := 2;
   end record;

end Tokens.OTokens;
Tokens.OTokens的主体是:

package body Tokens.OTokens is

   ------------------
   -- Root_Of_Tree --
   ------------------

   function Root_Of_Tree (T : Otoken) return Integer is
   begin
      return T.Y * 2;
   end Root_Of_Tree;

end Tokens.OTokens;
package tokens.vtokens is
   type vtoken is new token with private;
   function Root_Of_Tree(T : vtoken) return Integer;
private
   type vtoken is new token with record
      Z : Integer := 3;
   end record;

end tokens.vtokens;
Tokens.VTokens的规范为:

package body Tokens.OTokens is

   ------------------
   -- Root_Of_Tree --
   ------------------

   function Root_Of_Tree (T : Otoken) return Integer is
   begin
      return T.Y * 2;
   end Root_Of_Tree;

end Tokens.OTokens;
package tokens.vtokens is
   type vtoken is new token with private;
   function Root_Of_Tree(T : vtoken) return Integer;
private
   type vtoken is new token with record
      Z : Integer := 3;
   end record;

end tokens.vtokens;
正文标记。Vtokens是:

package body tokens.vtokens is

   ------------------
   -- Root_Of_Tree --
   ------------------

   function Root_Of_Tree (T : vtoken) return Integer is
   begin
      return T.Z * 2;
   end Root_Of_Tree;

end tokens.vtokens;
创建包含一个otoken和一个vtoken的数组的主要过程如下:

with Ada.Text_IO; use Ada.Text_Io;
with Tokens; use Tokens;
with Tokens.OTokens; use Tokens.OTokens;
with tokens.vtokens; use tokens.vtokens;

procedure Main is
   Ot : token_Access := new Otoken;
   Vt : token_access := new vtoken;
   Ta : Token_Array := (Ot, Vt);
begin
   for tk of Ta loop
      Put_Line(Integer'Image(Root_of_Tree(tk.all)));
   end loop;
end Main;
最好记住,OToken类型包含两个字段X和Y。VToken类型包含两个字段X和Z。 主程序的输出为:

4
6

您的困惑似乎包括包的使用和Ada中定义分派操作的方式。 调度操作必须在定义标记数据类型的同一包中定义,但在定义任何其他类型之前

package Tokens is
   type token is tagged private;
   function Root_Of_Tree(T : Token) return Integer;
   type Token_Access is access all Token'Class;
   type Token_Array is array (Positive range <>) of Token_Access;
private
   type Token is tagged record
      X : Integer := 1;
   end record;
end Tokens;
我使用了子包来定义Otoken和Vtoken类型

package Tokens.OTokens is
   type Otoken is new Token with private;
   function Root_Of_Tree(T : Otoken) return Integer;
private
   type Otoken is new Token with record
      Y : Integer := 2;
   end record;

end Tokens.OTokens;
Tokens.OTokens的主体是:

package body Tokens.OTokens is

   ------------------
   -- Root_Of_Tree --
   ------------------

   function Root_Of_Tree (T : Otoken) return Integer is
   begin
      return T.Y * 2;
   end Root_Of_Tree;

end Tokens.OTokens;
package tokens.vtokens is
   type vtoken is new token with private;
   function Root_Of_Tree(T : vtoken) return Integer;
private
   type vtoken is new token with record
      Z : Integer := 3;
   end record;

end tokens.vtokens;
Tokens.VTokens的规范为:

package body Tokens.OTokens is

   ------------------
   -- Root_Of_Tree --
   ------------------

   function Root_Of_Tree (T : Otoken) return Integer is
   begin
      return T.Y * 2;
   end Root_Of_Tree;

end Tokens.OTokens;
package tokens.vtokens is
   type vtoken is new token with private;
   function Root_Of_Tree(T : vtoken) return Integer;
private
   type vtoken is new token with record
      Z : Integer := 3;
   end record;

end tokens.vtokens;
正文标记。Vtokens是:

package body tokens.vtokens is

   ------------------
   -- Root_Of_Tree --
   ------------------

   function Root_Of_Tree (T : vtoken) return Integer is
   begin
      return T.Z * 2;
   end Root_Of_Tree;

end tokens.vtokens;
创建包含一个otoken和一个vtoken的数组的主要过程如下:

with Ada.Text_IO; use Ada.Text_Io;
with Tokens; use Tokens;
with Tokens.OTokens; use Tokens.OTokens;
with tokens.vtokens; use tokens.vtokens;

procedure Main is
   Ot : token_Access := new Otoken;
   Vt : token_access := new vtoken;
   Ta : Token_Array := (Ot, Vt);
begin
   for tk of Ta loop
      Put_Line(Integer'Image(Root_of_Tree(tk.all)));
   end loop;
end Main;
最好记住,OToken类型包含两个字段X和Y。VToken类型包含两个字段X和Z。 主程序的输出为:

4
6

首先,您需要将
rootOfTree
声明为抽象操作 令牌的:

type token is abstract tagged record
   x: integer;
end record;
function rootOfTree( t: tokensamp.token) return Integer is abstract;
(必须在启用
标记之前声明基本操作
冷冻,基本上是在使用它之前,比如在声明中
派生类型)

然后声明
otoken
vtoken
的基本操作;他们 必须在与其对应的类型相同的包中声明 是原始的,即可以分派给

type otoken is new token with record
   y: integer;
end record;

type vtoken is new token with record
   z: integer;
end record;

function rootOfTree( t: tokensamp.vtoken) return integer;
function rootOfTree( t: tokensamp.otoken) return integer;
(更正常的做法是在每个参数之后立即声明它们 键入,但由于两者都不冻结另一个,因此这是可以的)

请注意,
rootOfTree
操作都不是访问类型 参数

你不需要<代码> PoToMe</代码>,<代码> PVTu饰>代码>,尽管你可以考虑 在此处声明类范围指针:

type ptoken is access token'class;
然后,您需要为
包tokensamp
声明一个主体,其中包含 两个具体的
rootOfTree
s的实现

考虑到
parsesamp
,您不能在这里声明
rootOfTree

你可以写

result := tokensamp.rootOfTree (t1(i).all);
t1(i)
是指向类范围的指针,
。all
是类范围的值,并且
tokensamp.rootOfTree
是一个可调度的操作,因此这是一个 调度电话)

。。或者你可以用更漂亮的速记法

result := t1(i).rootOfTree;

首先,您需要将
rootOfTree
声明为抽象操作 令牌的

type token is abstract tagged record
   x: integer;
end record;
function rootOfTree( t: tokensamp.token) return Integer is abstract;
(必须在启用
标记之前声明基本操作
冷冻,基本上是在使用它之前,比如在声明中
派生类型)

然后声明
otoken
vtoken
的基本操作;他们 必须在与其对应的类型相同的包中声明 是原始的,即可以分派给

type otoken is new token with record
   y: integer;
end record;

type vtoken is new token with record
   z: integer;
end record;

function rootOfTree( t: tokensamp.vtoken) return integer;
function rootOfTree( t: tokensamp.otoken) return integer;
(更正常的做法是在每个参数之后立即声明它们 键入,但由于两者都不冻结另一个,因此这是可以的)

请注意,
rootOfTree
操作都不是访问类型 参数

你不需要<代码> PoToMe</代码>,<代码> PVTu饰>代码>,尽管你可以考虑 在此处声明类范围指针:

type ptoken is access token'class;
然后,您需要为
包tokensamp
声明一个主体,其中包含 两个具体的
rootOfTree
s的实现

考虑到
parsesamp
,您不能在这里声明
rootOfTree

你可以写

result := tokensamp.rootOfTree (t1(i).all);
t1(i)
是指向类范围的指针,
。all
是类范围的值,并且
tokensamp.rootOfTree
是一个可调度的操作,因此这是一个 调度电话)

。。或者你可以用更漂亮的速记法

result := t1(i).rootOfTree;

作为Jim Rogers和Simon Wright给出的答案的补充,如果你使用艾达2012,那么你可以考虑使用不定的持有者来构造你的数组(参见和

)。 如基本原理中所述,持有者是可以容纳(和管理)对象的单个实例的容器。当对象作为参数传递给
to_Holder
子程序(参见下面的示例)时,该对象被复制到堆实例中,而堆实例在不再需要时(例如,当它被替换或当Holder超出范围时)会被销毁。因此,holder容器使您不再像直接使用
access
类型时那样手动管理内存

(性能)成本是复制传递给
to_Holder
程序的对象。您可以在保持器之间“移动”对象(使用th中定义的
move
子程序)