我们说我有Planet
:
type Planet is tagged null record;
type Planet_Ref is access Planet'class;
现在我将其子类化:
type Habitable_Planet is new Planet with null record;
type Habitable_Planet_Ref is access Habitable_Planet'class;
现在我定义一些变量:
p: Planet_Ref := Make_Planet;
hp: Habitable_Planet_Ref := Make_Habitable_Planet;
我会天真地期望分配p := hp
会起作用,因为Habitable_Planet是Planet的子类。但当然不会起作用,因为用type
定义的每种类型都是不同的,并且不与任何其他类型互操作。
所以我希望必须声明Habitable_Planet_Ref是Planet_Ref的子类型才能使其工作。但语法似乎并不允许这样做。
我如何使这项工作?
(是的,我知道我可以使用显式视图转换将Habitable_Planet_Ref强制转换为Planet_Ref,但这真的很难看,而且我想避免它。)
答案 0 :(得分:4)
Ada按名称识别类型,所以你确实需要在这里进行视图转换。 但是,如果您使用的是Ada 2005,则可以使用匿名访问类型。例如:
hp: access Habitable_Planet'Class := Make_Habitable_Planet;
p: access Planet'Class := hp; -- valid with anonymous access types
使用匿名访问类型的一个缺点是代码更多 详细(虽然通常你不会将它们用于局部变量,但是 作为子程序的参数或(标记的)记录中的字段。 它们也不能与Unchecked_Deallocation一起使用。事实上,我个人经常 正是因为这样才使用它们:当我在一个记录中有一个字段时 匿名访问类型,我知道该记录不“拥有”访问过的数据, 因此它不应该释放它(事实上,我必须写一些令人费解的 代码来释放它们)。 当然,根据您的要求,类型匹配的结果稍微多一点 放松,这也很好。
答案 1 :(得分:0)
ajb在他的评论中是正确的。 Ada对于您在其他语言中习惯的许多实践来说过于严格。另一种方法是不使用对象,而只是简单的记录或区分记录。我知道这可能不是你想要的,但根据我的经验,可以用更少的代码行完成更多的工作,而且解决方案让我更容易理解。
简单记录
--...
type Rec_Planet is record
--.. stuff
end record;
--...
type Rec_Habitable_Planet is record
Planet : Rec_Planet := (others => <>);
--.. stuff
end record;
判别记录
type Enum_Planet is (Normal_Planet, Habitable_Planet);
type Rec_Planet(Kind : Enum_Planet := Normal_Planet) is record
-- rec_Planet stuff..
case Kind is
when Habitable_Planet => -- Rec_Habitable_Planet stuff
when others => null;
end case;
end record;
答案 2 :(得分:0)
所以@manuBriot给了我我需要的答案,但在我的问题中还有其他一些我做错的事情我应该澄清,因为他们会混淆其他读这个问题的人。
我通过使用访问来混淆这个问题。从Ada的角度来看,使用type
定义的所有访问都是截然不同的,所以它永远不会看到访问指向的内容;它只是不允许任务。
然而,Ada支持隐式向上扩展类范围的类型(以及离散类型,其中子类型的实例将被隐式转换为其父类型 - 实现所有类层次结构!但是那个&#39; s这里不太相关。)这里的例子:
With Ada.Text_IO; Use Ada.Text_IO;
With Ada.Integer_Text_IO; Use Ada.Integer_Text_IO;
procedure Prog is
package Superclass is
type Class is tagged record
null;
end record;
procedure Announce(self: in out Class);
subtype Var is Class'class;
end;
package body Superclass is
procedure Announce(self: in out Class)
is
begin
Put_Line("I am the superclass");
end;
end;
package Subclass is
type Class is new Superclass.Class with null record;
procedure Announce(self: in out Class);
end;
package body Subclass is
procedure Announce(self: in out Class)
is
begin
Put_Line("I am the subclass");
end;
end;
osuper: Superclass.Class;
osub: Subclass.Class;
vsuper: Superclass.Var := osuper;
vsub: Superclass.Var := osub; -- implicit upclass here
begin
vsuper.Announce;
vsub.Announce;
end;
(它在这里的意思是:http://ideone.com/M79l0a有趣的旁注。如果你在Prog包中定义subtype Var is Superclass.Var
,然后在vsuper和vsub的定义中使用Var,那就是#one;&#39; s Ada编译器崩溃。)
当然,像所有不定类型一样,一旦变量被初始化,那么它的类型就不能改变。所以我可以将任何Subclass.Object分配给vsub,但是我不能为它分配一个Superclass.Object。当然,我在物理上复制对象而不是在别处引用一个对象。
对类范围类型的隐式向上转换访问应该是安全的。因为分配给类范围的类型进行运行时实例检查以确保对象的物理类型是兼容的,所以不应该像在C ++中那样意外地破坏对象---请参阅Overwriting an instance of a subclass with an instance of a superclass , 例如。因此,分配到解除引用的访问不应该是一个问题。然而,它在晚上2100,我的大脑变成了污泥,所以我完全有可能在这里遗漏一些东西。 (虽然假设使用匿名访问时没有任何问题,我怀疑没有。)Elucidation welcome ...