从C++和Java到Ada的类公式化概念

2020年9月16日 14点热度 0条评论

也许C++和Java人士可以帮助我定义我要解释的问题。我在Ada中有一个问题(您不需要知道,我只是对这个概念感兴趣),该如何表示一个类的构造方法,该类实现了动态标识符的三个主要分支:

  • 纯数字值(int,float,String等)
  • 列表/堆栈项
  • 在C++中可能是线程(在Ada中,它与任务相关的概念更广泛,但是我们可以将简单任务定义为线程,因此该概念也适用)
  • 我将把这个类称为
    Par_Class,并将其作为任何构造对象调用
    Par_Obj。因此,当创建对象
    Par_Obj(因此,将初始化数值值,为列表/堆栈分配了其他列表/堆栈或为null且保留了线程执行的内存范围)时,操作系统会自动开始执行新的与我的主应用程序并行的线程(现在它们争用系统资源)。但是为了简化示例,我们假设我有一个带有整数和指向字符串的指针的类。

    例如,在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。但这很明显,因为我要做的只是初始化对象(因此它不再为null)。还有其他构造对象的方法,例如,使用类外部的函数,但是我不想使用这种方法,因为它与体系结构背道而驰。您能否帮助我向我的Ada friend 提出问题,以便他们可以帮助我在Ada中实现它?我想我很难用一般概念来解释这一点。谢谢。

    回答

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

  • “是否可以在Par_Class中声明“静态”函数?和“有没有办法让非成员函数声明为Par_Class的 friend ?”
  • 我可以用“是否可以在标记类型中声明“静态”函数来完成它?而且,声明该类的包可以充当 friend 还是静态函数?”

    更新

    如@SimonWright和comp.lang.ada论坛的一些人所建议的那样,有一些更好的理由说明为什么要实现它:

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

    所以我问:在这种情况下,构造函数将表现为C++静态函数(或者可能是一个 friend 函数?)?

    德米特里·卡扎科夫(Dmitry Kazakov)回答:

    That depends on what you mean. In Ada:

    1. there is no hidden parameters

    2. an operation can be dispatching (virtual) in any combination of parameters and/or result. But an operation cannot be dispatching in more than one type (no multiple dispatch). All tags of dispatching parameters must be same (no multi-methods).

    3. there is no static or friend operations as the visibility rules are based on packages.

    The function Construct above is a primitive operation, it is not a constructor.

    Constructors in Ada are implicit, they consist of

    1. construction of the components (in an unspecified order, recursively);

    2. a call to Initialize if the type is a descendant of Ada.Finalization.[Limited_]Controlled. (Overridden bodies of Initialize are not called! I.e. Ada constructors do not traverse derivation path. In short, aggregation is safe, derivation is not;

    3. starting all task components. (Note, tasks are not running when Initialize is called!)

    Destructors act in the reverse order: tasks - Finalize - components.

    而且我想它可以回应。谢谢大家

    解决方案如下:

    关于Ada构造函数?
    很难解释这个概念,因为我不了解Ada语言的哲学,局限性和优势。不过,猜测Ada中没有构造函数。
    (非 friend ?)非成员函数?
    我猜这不是您想要的解决方案:

  • 具有InitializePar_Class成员函数,该函数设置Par_Class的私有(private)数据
  • 有一个Par_Class_Constructor非成员函数调用,该函数调用了初始化函数
  • 但是此解决方案并不令人满意,因为它将
    Initialize公开为公共(public)方法,这违反了封装(任何人都可以随时调用该方法,这几乎与公开所有数据一样愚蠢)。

    静态成员函数?

    您要做的是仅通过一个函数调用即可分配和初始化代码,而不会破坏封装。

    您(正确地)觉得此函数应该是
    Par_Class接口(interface)的一部分,因此,您想在
    Par_Class'声明中声明它(这将产生使它可以访问
    Par_Class私有(private)成员变量的有趣副作用)

    在Java或C++中,除非使用构造方法,否则可以通过使用静态方法(即类的方法)而不是实例的方法来解决。此方法是
    static,因此无法访问
    this,这意味着您可以调用它而无需
    Par_Class实例。

    因此,您对Ada friend 的问题可能是:有没有办法在
    Par_Class中声明一个“静态”函数?

    ( friend ?)非成员函数?

    具有类似效果(如果不是相似的语法糖)的另一种方法是让非成员函数发挥作用。在C语言中,您将拥有类似于
    Par_Class_Constructor的功能,它将是一个返回指向Par_Class类型的
    struct的指针的函数。

    在C++中,您可以使用与Java相同的技巧或与C相同的技巧。在最后一种情况下,
    Par_Class_Constructor将被声明为
    friend类的
    Par_Class来访问其私有(private)数据,或者可以调用具有访问该私有(private)数据。

    这样,您仍然可以使用一个函数分配和初始化对象,并仍然保护类的封装(因为此方法返回一个新对象,而不是像上面描述的令人不满意的
    Initialize方法那样对其进行修改)

    因此,如果您可以使用非成员函数,那么另一种可能性可能是:有没有办法将非成员函数声明为
    friend
    Par_Class

    编辑

    注意:我以前的主题不正确的答案...我真的应该上床睡觉...

    我不认识Ada,但请阅读您的代码:

    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?

    有点像(我从您的Integer初始化代码中推断出):

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

    ???