Концепция формулировки классов от C ++ и Java до Ada - PullRequest
5 голосов
/ 10 ноября 2011

Может быть, C ++ и Java люди могут помочь мне определить эту проблему, которую я собираюсь объяснить.У меня есть проблема в Аде (вам не нужно это знать, мне просто интересна концепция) о том, как представить конструктор класса, который реализует три основные ветви динамических идентификаторов:

  • Чистые числовые значения (int, float, String, что угодно)
  • Элемент списка / стека
  • Что-то, что в C ++, вероятно, thread (в Ada у нас есть болееШирокая концепция этого, относящаяся к задача , но мы можем представить простую задачу как поток, так что концепция также применима)

Я назову этот класс Par_Class, и любой построенный объект вызывает Par_Obj.Таким образом, когда создается объект Par_Obj (то есть, числовые значения инициализируются, списки / стеки имеют другие списки / стеки, выделенные или равные нулю, и диапазон памяти для выполнения потока зарезервирован), ОС автоматически запускает выполнениеновый поток параллельно с моим основным приложением (и теперь они борются за системные ресурсы).Но чтобы упростить пример, давайте предположим, что у меня есть класс с целым числом и указателем на строку.

В 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;

И вот в чем проблема.Компилятор говорит мне, что я не мог использовать метод Construct в Par_Obj := Par_Obj.Construct, потому что все же мой объект нулевой.Но это так очевидно, потому что я просто хочу инициализировать объект (чтобы он больше не был нулевым).Существуют и другие способы конструирования объекта, например, использование функции извне класса, но я не хочу использовать этот подход, потому что он уходит от архитектуры.Не могли бы вы помочь мне сформулировать проблему моим друзьям по Аде, чтобы они помогли мне реализовать ее в Аде?Я думаю, мне сложно объяснить это в общих чертах.Спасибо.

Ответ

@ paercebal дал мне то, что, как я думаю, могло бы достичь моей цели:

  • "Есть ли способ получить"статическая" функция объявлена ​​внутри Par_Class? "и "есть ли способ сделать функцию, не являющуюся членом, объявленной другом Par_Class?"

Я мог бы дополнить это "есть ли способ объявить" статическую "функцию внутри тегового типа? Кроме того, может ли пакет, в котором объявлен класс, действовать как друг или как статическийfunction? "

Обновление

У вас есть еще несколько веских причин для его реализации в соответствии с предложением @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:

  1. нет скрытых параметров

  2. операция может быть диспетчеризована (виртуально) в любой комбинации параметров и / или результата.Но операция не может быть отправлена ​​более чем в один тип (без многократной отправки).Все теги параметров диспетчеризации должны быть одинаковыми (без мульти-методов).

  3. нет статических или дружественных операций, поскольку правила видимости основаны на пакетах.

Функция Construct выше является примитивной операцией, а не конструктором.

Конструкторы в Аде неявные, они состоят из

  1. построение компонентов (в неуказанном порядке, рекурсивно);

  2. вызов Initialize, если тип является потомком Ada.Finalization. [Limited_] Управляется. (Переопределены тела Инициализация не называется! То есть Ада конструкторы не пересекают путь деривации. Короче говоря, агрегация безопасна, деривация - нет;

  3. запуск всех компонентов задачи. (Обратите внимание, что при вызове Initialize задачи не выполняются!)

Деструкторы действуют в обратном порядке: задачи - Завершение - компоненты.

И я думаю, это отвечает. Спасибо, люди.

Ответы [ 3 ]

4 голосов
/ 10 ноября 2011

Par_Class (int aValue, const char *aName) - это особый синтаксис конструктора C ++; при использовании компилятор генерирует новую пустую область памяти (вызывая malloc(), если в стеке есть new, в противном случае) и конструктор заполняет ее.

Это , а не так же, как

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);

, который не будет работать так же, как ваш текущий код Ada, если вызывается с неинициализированным Par_Class *.

Ближайший (Ада 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;

или, ОК для Ады 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 голосов
/ 11 ноября 2011
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;

Обратите внимание на изменения; конструктор теперь возвращает правильный тип [pointer] и никогда не позволяет ему быть нулевым.

Далее конструкция объекта перемещается в раздел инициализации, а не в тело. Таким образом, это гарантирует, что объект [-pointer] никогда не будет нулевым.

2 голосов
/ 10 ноября 2011

О конструкторах Ada?

Сложно объяснить концепцию, поскольку я понятия не имею о философии, ограничениях и сильных сторонах языка Ada.Тем не менее, предположим, что в Ada нет конструкторов.

A (не-друг?) Функция, не являющаяся членом?

Это не то решение, которое вам нужно, я думаю:

  • с Initialize функцией-членом Par_Class, которая устанавливает личные данные Par_Class
  • с Par_Class_Constructor функцией, не являющейся членом, вызывающей эту функцию Initialize

Но это решение не удовлетворяет, потому что оно выставляет Initialize как открытый метод, который является нарушением инкапсуляции (любой может вызвать этот метод в любой момент, что почти так же глупо, как сделать всеобщедоступные данные).

Статическая функция-член?

Что вы хотите сделать, это выделить и инициализировать ваш код с помощью одного вызова функции, без нарушения инкапсуляции .

Вы чувствуете (справедливо), что эта функция должна быть частью Par_Class интерфейса , и, таким образом, вы хотите объявить ее в объявлении Par_Class ' (котороебудет иметь интересный побочный эффект от предоставлениясс к Par_Class приватные переменные-члены)

В Java или в C ++, для запрета конструкторов, это можно решить, используя статический метод, то есть метод класса, а не метод экземпляра,Этот метод static, и поэтому не имеет доступа к this, что означает, что вы можете вызвать его, не нуждаясь в экземпляре Par_Class.

Итак, ваш вопрос вашим друзьям по Аде может быть: Есть ли способ объявить «статическую» функцию внутри Par_Class?

A (друг?) функция, не являющаяся членом?

Другой способ получить аналогичный эффект (еслине похожий синтаксический сахар) будет иметь функцию, не являющуюся членом, которая делает свое дело.В C у вас будет что-то вроде: Par_Class_Constructor, которая будет функцией, возвращающей указатель на struct типа Par_Class.

В C ++ вы можете использовать тот же трюк, что и Java, илитот же прием, что и в C. В этом последнем случае Par_Class_Constructor будет объявлено friend класса Par_Class, чтобы иметь доступ к его частным данным, или может вызвать метод члена инициализации, который имеет доступ к этим частным данным.

Таким образом, вы по-прежнему можете выделять и инициализировать свой объект с помощью одной функции и по-прежнему защищать инкапсуляцию вашего класса (так как этот метод возвращает новый объект, а не модифицирует его как неудовлетворительный Initialize метод, описанный выше)

Таким образом, если для вас не подходит функция, не являющаяся членом, другая возможность может быть: есть ли способ объявить функцию, не являющуюся членом, friend из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 таким же образом?

Что-токак (я понял из вашего кода инициализации Integer):

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

???

...