首页 文章

从C和Java到Ada的类制定概念

提问于
浏览
5

也许C和Java人员可以帮我定义这个我要解释的问题 . 关于如何表示实现动态标识符的三个主要分支的类的构造函数,我在Ada(你不需要知道它,我只是对这个概念感兴趣)有一个问题:

  • 纯数值(int,float,String,等等)

  • 列表/堆栈项目

  • C中的东西可能是一个线程(在Ada中我们有一个更广泛的概念,与任务相关,但我们可以将一个简单的任务概念作为一个线程,所以这个概念也适用)

我要调用这个类 Par_Class ,并且是任何构造的对象调用 Par_Obj . 因此,当创建对象 Par_Obj 时(因此,数值初始化,列表/堆栈分配了其他列表/堆栈或为空并且保留了线程执行的内存范围),OS自动开始执行新的线程与我的主应用程序并行(现在他们争夺系统资源) . 但是为了简化示例,让's suppose I' d有一个带有整数的类和一个指向字符串的指针 .

例如,在C中,我可以编码(如果我做错了,请纠正我)

class Par_Class {
public:
  Par_Class (int aValue, const std::string & aName);

private:
  int theValue;
  std::string theName;
};

构造函数可以实现为

Par_Class::Par_Class (int aValue, const std::string & aName)
  : theValue(aValue)
  , theName(aName)
{
}

最后我们可以实例化这个类

Par_Class Par_Obj (23, "My object is this");

并确保此构造方法属于Par_Class类,而不属于任何其他类 .

同样,在Java中,我们可以编码

public class Par_Class {
  private int theValue;
  private String theName;

  public Par_Class (int aValue, String aName){
    theValue = aValue;
    theName = aName;
  }
};

我们可以使用实例化对象

Par_Class Par_Obj = new Par_Class (23, "My object is this");

(如果我错了,请再次纠正我) . 同样, Par_Class 构造函数是类 Par_Class 的方法 .

在Ada 2005中,这个类可以编码为

--par_pkg.ads
package Par_Pkg is
   type Par_Class is tagged private;
   type Par_Class_Ptr is access all Par_Class;
   type Integer_Ptr is access Integer;

   function Construct 
     (P : access Par_Class; aValue : Integer; aName : Integer_Ptr)
      return Par_Class_Ptr;

private
   type Par_Class is tagged
      record
         theValue : Integer;
         theName  : Integer_Ptr;
      end record;
end Par_Pkg;

-- par_pkg.adb
package body Par_Pkg is
   function Construct 
     (P : access Par_Class; aValue : Integer; aName : Integer_Ptr)
      return Par_Class_Ptr is
      pragma Unreferenced (P);
      P_Ptr : constant Par_Class_Ptr := new Par_Class;
   begin
      P_Ptr.theValue := aValue;
      P_Ptr.theName := aName;
      return P_Ptr;
   end Construct;

end Par_Pkg;

并且用户可以打电话

with Par_Pkg; use Par_Pkg;
procedure Par_Main is
   Par_Obj : Par_Class_Ptr;
   Int_Obj : Integer_Ptr;
begin
   Int_Obj := new Integer;
   Int_Obj.all := 12; -- don't worry about been string or integer
   Par_Obj := Par_Obj.Construct 
     (aValue => 23,
      aName => Int_Obj);
end Par_Main;

这就是问题所在 . 编译器告诉我,我无法在 Par_Obj := Par_Obj.Construct 中使用方法Construct,因为我的对象是null . 但是它想要使用这种方法,因为它远离架构 . 你能帮我把这个问题告诉我的Ada朋友,这样他们可以帮我在Ada中实现吗?我想我在一般概念术语中解释这个问题有点困难 . 谢谢 .

Answer

@paercebal给了我我认为可以实现我的目标:

  • "Is there a way to have a "静态" function declared inside Par_Class?"和"is there a way to have an non-member function declared friend of Par_Class?"

我可以完成它“有没有办法在标记类型中声明一个”静态“函数?另外,声明类的包可以充当朋友还是静态函数?”

Update

根据@SimonWright和comp.lang.ada论坛的一些人的建议,为什么实现它有一些更好的理由:

function Construct (aValue: Integer; aName: Integer)
                    return Par_Class is
begin
  return (theValue => aValue,
          theName  => aName);
end Construct;

所以我问:在这种情况下,函数Construct会表现为C静态函数(或者可能是朋友吗?)?

德米特里卡扎科夫回答说:

这取决于你的意思 . 在Ada中:没有隐藏的参数,操作可以在任何参数和/或结果的组合中调度(虚拟) . 但是操作不能在多种类型中调度(没有多次调度) . 调度参数的所有标签必须相同(没有多方法) . 没有静态或朋友操作,因为可见性规则基于包 . 上面的函数Construct是一个原始操作,它不是构造函数 . Ada中的构造函数是隐含的,它们由组件的构造(以未指定的顺序,递归地)组成;如果类型是Ada.Finalization的后代,则调用Initialize . [Limited_]受控制 . (不会调用重写的Initialize主体!即Ada构造函数不会遍历派生路径 . 简而言之,聚合是安全的,派生不是;启动所有任务组件 . (注意,调用Initialize时任务不会运行!)析构函数在相反的顺序:tasks - Finalize - components .

我猜它会回应 . 谢谢大家

3 回答

  • 2

    关于Ada构造函数?

    由于我不知道Ada语言的哲学,局限性和优势,因此很难解释这个概念 . 仍然,猜测Ada中没有构造函数 .

    A(非朋友?)非会员功能?

    这不是你想要的解决方案我想:

    • 具有 Par_ClassInitialize 成员函数,用于设置 Par_Class 的私有数据

    • Par_Class_Constructor 非成员函数调用Initialize函数

    但是这个解决方案并不令人满意,因为它会将 Initialize 暴露为公共方法,这是一个 breach of encapsulation (任何人都可以在任何时刻调用该方法,这几乎与使所有数据公开一样愚蠢) .

    静态成员函数?

    你想要做的是用 one function call, without breaching encapsulation 分配和初始化你的代码 .

    You feel (rightly) this function should be part of Par_Class interface ,因此,你想要 declare it inside the Par_Class' declaration (这会产生有趣的副作用,即允许它访问 Par_Class 私有成员变量)

    在Java或C中,禁止构造函数,这可以通过使用静态方法(即类的方法)而不是实例的方法来解决 . 此方法是 static ,因此无法访问 this ,这意味着您可以不需要 Par_Class 的实例就可以调用它 .

    那么,你对Ada朋友的问题可能是: is there a way to have a "static" function declared inside Par_Class?

    A(朋友?)非会员功能?

    另一种具有相似效果的方法(如果不是类似的语法糖)将是一个非成员函数来做这个技巧 . 在C中,你会有类似的东西: Par_Class_Constructor ,这是一个函数,返回指向Par_Class类型的 struct 的指针 .

    在C中,您可以使用与Java相同的技巧,或者使用与C相同的技巧 . 在最后一种情况下, Par_Class_Constructor 将被声明 Par_ClassPar_Class 以访问其私有数据,或者可以调用初始化成员方法访问该私人数据 .

    这样, you can still allocate and initialize your object with one function,and still protect the encapsulation of your class (因为此方法返回一个新对象,而不是像上面描述的令人不满意的 Initialize 方法那样修改它)

    所以,如果你有一个非成员函数是好的,另一种可能性是: is there a way to have an non-member function declared friend of Par_Class?

    编辑

    注意:我之前的,主题外答案......我真的应该去睡觉了......

    我不知道阿达,但阅读你的代码:

    with Par_Pkg; use Par_Pkg;
    procedure Par_Main is
       Par_Obj : Par_Class_Ptr;
       Int_Obj : Integer_Ptr;
    begin
       Int_Obj := new Integer;
       Int_Obj.all := 12; -- don t worry about been string or integer
       Par_Obj := Par_Obj.Construct 
         (aValue => 23,
          aName => Int_Obj);
    end Par_Main;
    

    我看到Int_Obj已经分配了 new Integer 语句 .

    你不需要以同样的方式分配Par_Obj吗?

    像(我从你的整数初始化代码推断):

    Par_Obj := new Par_Class_Ptr      -- allocate ?
       Par_Obj.all := Par_Obj.Construct  -- initialize ?
         (aValue => 23,
          aName => Int_Obj);
    

    ???

  • 4

    Par_Class (int aValue, const char *aName) 是非常特殊的C构造函数语法;当使用时,编译器会生成一个新的内存空白区域(如果在堆栈上有 new 则调用 malloc() ,否则构造函数会填充它) .

    这跟不一样

    function Construct 
      (P : access Par_Class; aValue : Integer; aName : Integer_Ptr)
       return Par_Class_Ptr;
    

    这需要有一个以前的 Par_Class 实例,就像有一个C成员函数

    Par_Class *a_Par_Class(int aValue, const char *aName);
    

    如果使用未初始化的 Par_Class * 调用,它将以与当前Ada代码相同的方式失败 .

    我认为最接近(Ada 2012)相当于构造函数

    function Construct 
      (aValue : Integer; aName : Integer_Ptr)
       return Par_Class is
    begin
       return Result : Par_Class do
          Result.theValue := aValue;
          Result.theName := aName;
       end return;
    end Construct;
    

    或者,对于Ada 95/2005并且最接近您现有的计划:

    function Construct 
      (aValue : Integer; aName : Integer_Ptr)
       return Par_Class_Ptr is
       P_Ptr : constant Par_Class_Ptr := new Par_Class;
    begin
       P_Ptr.theValue := aValue;
       P_Ptr.theName := aName;
       return P_Ptr;
    end Construct;
    

    @paercebal提到你需要一个静态成员函数,这是正确的; C构造函数是我认为这个的语法糖 . 在Ada中,范围确定是在包级别完成的,因此 function Construct - 在同一个包的公共部分中声明并返回 Par_Class_Ptr - 与 Par_Class 无关紧要(在架构上) .

  • 3
    With
        Ada.Text_IO;
    
    Procedure Test is
      package Par_Pkg is
      type Par_Class is tagged private;
      type Par_Class_Ptr is access all Par_Class;
      type Integer_Ptr is access Integer;
    
      function Construct 
        (aValue : Integer; aName : Integer_Ptr)
      return Par_Class_Ptr;
    
       private
       type Par_Class is tagged
         record
            theValue : Integer;
            theName  : Integer_Ptr;
         end record;
    end Par_Pkg;
    ----------------------------
    package body Par_Pkg is
      function Construct 
        (aValue : Integer; aName : Integer_Ptr)
      return Par_Class_Ptr is
         --P_Ptr : constant Par_Class_Ptr := new Par_Class;
      begin
         Return Result: Par_Class_Ptr:= New Par_Class'( others => <> ) do
            Result.theValue := aValue;
            Result.theName := aName;
         End Return;         
      end Construct;
    
    end Par_Pkg;
    ----------------------------
    use Par_Pkg;
    
       Int_Obj : Integer_Ptr:= new Integer'(12);
       Par_Obj : Par_Class_Ptr:= Construct(aValue => 23, aName => Int_Obj);
    
    
    Begin
       Ada.Text_IO.Put_Line( "Everything went fine!" );
    exception
       when others => 
          Ada.Text_IO.Put_Line("Something went horribly wrong!");
    End Test;
    

    注意变化;构造函数现在返回正确的[指针]类型,并且永远不允许它为null .

    此外,对象的构造被移动到初始化部分而不是主体 . 所以这确保了对象[-pointer]永远不会为空 .

相关问题