在Ada中使用私人类型

时间:2014-01-15 04:08:31

标签: ada

我正在尝试在Ada中实现一个私有类型,就像在Java或C ++中使用它一样。例如,我想在Ada中拥有一个“私人int”。

正如我在http://en.wikibooks.org/wiki/Ada_Programming/Object_Orientation#Encapsulation:_public.2C_private_and_protected_members找到的那样,我必须在包体中实现我的变量。所以我试图像在示例中描述的那样实现它。 我的问题是我无法创建一个用于创建对象和设置值的Create函数。我的第二个问题是我想要这种类型的孩子。这个子元素应该有一个Create函数,它设置masterclass和子类的值。

这是我的代码无效。我已经对那些无法解决的问题添加了评论。

private with Ada.Finalization;
package MasterClass is
    type MasterC is tagged private;    
    type MasterC_Class_Ref is access all MasterC'Class;
    procedure printNumObjects;
    procedure do( This : in MasterC );
    function Make (f1, f2 : Float) return MasterC;
    function getVar(This : MasterC) return float;  
private
    type Private_Part;  -- declaration stub
    type Private_Part_Pointer is access Private_Part;

    type Private_Component is new Ada.Finalization.Controlled with record
        P: Private_Part_Pointer;
    end record;
    overriding procedure Initialize (X: in out Private_Component);
    overriding procedure Adjust     (X: in out Private_Component);
    overriding procedure Finalize   (X: in out Private_Component);

    type MasterC is tagged record
        P : Private_Component;
    end record;
end MasterClass;

package body MasterClass is
    numObjects : Integer := 0;      
    type Private_Part is record  -- complete declaration
        fTest1, fTest2 : float := 0.0;
    end record;

    overriding procedure Initialize (X: in out Private_Component) is
    begin
        X.P := new Private_Part'(fTest1=>0.0, fTest2 => 0.0);
    end Initialize;

    overriding procedure Adjust (X: in out Private_Component) is
    begin
        X.P := new Private_Part'(fTest1 => X.P.fTest1, fTest2 => X.P.fTest2);
    end Adjust;

    overriding procedure Finalize (X: in out Private_Component) is
        procedure Free is new Ada.Unchecked_Deallocation (Private_Part, Private_Part_Pointer);
    begin
        Free (X.P);
    end Finalize;

    function Make (f1, f2 : Float ) return MasterC is   
    begin
        numObjects := numObjects + 1;
        return new MasterC'(fTest1=>0.0, fTest2=>0.0); -- This is not working!      
    end Make;

    procedure do( This : in MasterC ) is
    begin
        Put_Line( "doneMaster");
    end do;

    function getVar( This : MasterC )
        return float is
    begin
        return This.P.P.fTest1;  -- This is not working!
    end getVar; 
end MasterClass;

with MasterClass; use MasterClass;
package SubClass is 
    type SubC is new MasterClass.MasterC with 
        record
            fExtra1 : float := 0.0;
            fExtra2 : float := 0.0;
        end record;     
    type SubC_Class_Ref is access all SubC'Class;   
    overriding procedure do(This : SubC);
    function Make (f1, f2 : Float) return SubC; 
    function Make1 (f1, f2 , w, h: Float) return SubC;
end SubClass;

with MasterClass; use MasterClass;
package body SubClass is
    function Make ( f1, f2 : Float ) return SubC is
    begin       
        return ((fTest1 => f1, fTest2 => f2, fExtra1 => 0.0, fExtra2 => 0.0));      
    end Make;

    function Make1 ( f1, f2, w, h: Float ) return SubC is
    begin       
        return (fTest1 => f1, fTest2 => f2, fExtra1 => w, fExtra2 => h);        
    end Make1;

    overriding procedure do( This : in SubC ) is
    begin
        do(MasterC(This));
        Put_Line( "doneSub");
    end do;
end SubClass;

我无法找到任何有关如何实施此信息的信息。

我不知道如何实现的行是Make函数中的返回行

return new MasterC'(fTest1=>0.0, fTest2=>0.0); -- This is not working!

然后读取值的行

return This.P.P.fTest1;  -- This is not working!

然后是子类的Make函数中的行。

return ((fTest1 => f1, fTest2 => f2, fExtra1 => 0.0, fExtra2 => 0.0)); -- This is not working!      

2 个答案:

答案 0 :(得分:4)

ajb的回答是 非常 好,但我希望你能让我“努力”到Ada的OOP设施,因为它们实际上与Java完全不同等价物。 - 我在这里输入了我的头顶,所以这些例子可能无法编译,但应该将这些想法贯穿其中。

Package Test_1 is
  Type Point is record
   x, y : Integer;
  end record;
  -- Some operations
End Test_1;

在上面我们创建了一个类型,但是这里存在一个问题:它暴露了实现,用户可以直接更改值,如果需要确保某些进程/属性,这可能是灾难性的。 (它还引入了对实现细节的依赖。)

为了解决这个问题,我们可以将记录设为私有,并使客户端使用函数和过程来改变内部状态 - 就像getter和setter一样(但我们还没有涉及任何OOP)。

Package Test_2 is
  Type Point is private;

  Function Create( x, y : Integer ) return Point;
  Function Get_X ( P : Point ) return Integer;
  Function Get_Y ( P : Point ) return Integer;
  -- Some more operations.
Private
  Type Point is record
   x, y : Integer;
  end record;

  -- Ada 2012 expression-functions, for brevity.
  Function Create( x, y : Integer ) return Point is ( x => x, y => y );
  Function Get_X ( P : Point ) return Integer is ( P.X );
  Function Get_Y ( P : Point ) return Integer is ( P.Y );
End Test_2;

在上面进行了更改,以便实现是私有的,这允许我们在不强制客户端重新编译的情况下更改实现(见下文)。 (我们可以将Test_3更改为Test_2,它仍可以使用。)

Package Test_3 is
  Type Point is private;

  Function Create( x, y : Integer ) return Point;
  Function Get_X ( P : Point ) return Integer;
  Function Get_Y ( P : Point ) return Integer;
  -- Some more operations.
Private
  Type Point is record
   x, y : Float;
  end record;

  -- Ada 2012 expression-functions, for brevity.
  Function Create( x, y : Integer ) return Point is
  ( x => Float(x), y => Float(y) );
  Function Get_X ( P : Point ) return Integer is
  ( Integer(P.X) );
  Function Get_Y ( P : Point ) return Integer is
  ( Integer(P.Y) );
End Test_3;

因此,正如您所看到的,private意味着Ada与Java中的不同。因为在添加OOP之前,Ada有private这个概念(在Ada 95中),它被转移到了OOP。 (基本上这个想法是私有类型的实现中的更改不会导致程序将状态更改为合法或非法的结果。)

正如你所看到的,Ada也有记录和间接访问[私有类型]甚至继承[派生类型]的概念......但Ada仍然有类型的概念,特别是可以禁止的类型交互(长度的类型和权重的另一种类型),子类型[具有附加约束的类型]是不同的,它们不适合,因此它们需要某种方式来区分类型和“类型” -or-something-derived-from-it“这是'class属性的来源:Type_Name'Class表示tagged-type的继承树的整个分支。

-- Declare an operation that must be overridden in derived types,
-- and which takes the specific type:
Function Operation( Input : Type_Name ) return Boolean is abstract;

-- Declare an operation that takes the whole class: 
Function Operation( Input : Type_Name'Class ) return Boolean;

由于上述规则,具有某些私有组件的记录需要位于规范的私有部分中,以便对其内部结构的更改不会影响客户端的合法性。

现在我们大致类似于你在Java中习惯使用的东西:类定义和继承,但有了这个额外的想法,private是处理客户端使用(和包)的东西而不是严格地说,对象(或类型)本身。

答案 1 :(得分:2)

与Java不同,您不需要new来创建对象。 new专门分配对象并向对象返回访问。所以这个:

    return new MasterC'(fTest1=>0.0, fTest2=>0.0); -- This is not working!      

失败,因为声明函数返回MasterC,而不是access MasterC或任何声明为access MasterC(或access all MasterC'class或任何类型的类型)的类型。该函数根本不返回访问类型。所以不要使用new。相反,表单将是

    return (P => ???);  -- keep reading to see what ??? is

此处P的值为PrivateComponent。这是Ada.Finalization.Controlled的子类型,附加组件P,因此语法看起来像

return (P => (Ada.Finalization.Controlled with P => ???))

P的值为Private_Part_Pointer 是一种访问类型(Private_Part),所以在哪里使用new

return (P => (Ada.Finalization.Controlled with P => 
    new Private_Part' (ftest1=>0.0, ftest2=>0.0)));

真的不是那么复杂。要创建记录类型的值,请将值放在括号中,并使用 component-name =>,后跟每个组件的值。要创建访问类型的值,请使用new

返回SubC时,事情变得有趣。 SubCMasterC具有相同的组件,加上您添加的两个组件。因此,它有三个组成部分:PfExtra1fExtra2。但是,您不能在此处使用名称P,因为它位于MasterClass的私有部分,因此SubClass不可见。要创建SubC,您必须调用函数来创建MasterC的{​​{1}}部分。结果看起来像

SubC

[注意:这称为扩展聚合。]

这使用return (MasterClass.Make(f1, f2) with fExtra1 => 0.0, fExtra2 => 0.0); 创建MasterClass.Make,其中包含MasterC个组件; P函数结果使用SubC组件与您指定的PfExtra1来创建fExtra2所需的三个组件。

免责声明:我没有对此进行过测试(我在家里并且没有编译器方便),所以我可能会遇到一些错误的语法,或者可能还有其他一些我错过的细微错误。

编辑:既然我可以使用编译器,我发现上面两个更改使你的源代码编译(第二个更改必须在两个地方实现);除了SubC是保留字并且不能用作过程名称,do需要一个正文。这句话:

printNumObjects

编译好我的编译器。