Создание объектов с использованием примитивной операции - PullRequest
2 голосов
/ 26 марта 2019

Мой вопрос содержит более длинный фрагмент кода Ады.В основном я пытался смоделировать сообщение с заголовком объектно-ориентированным способом.Две примитивные операции Create_Message и Create_Message_Access могут использоваться для создания конкретных объектов сообщений.Я не совсем уверен, должна ли примитивная операция возвращать тип Message_Type или Message_Type_Access.Какой тип рекомендуется (эффективность?) Или оба решения не оптимальны?

Я думаю, что первым способом объект создается в стеке, а затем копируется после выполнения оператора return, поскольку переменная Object выходит за рамки.Правильно или нет?

function Create_Message (Title : String) return Message_Type is
   Object : Message_Type;
begin
   Object.Title := To_Unbounded_String (Title);

   return Object;
end Create_Message;

Я думаю, что во втором случае объект создается в куче, а затем указатель копируется после выполнения оператора return, поскольку переменная Object выходит за рамки.Правильно или нет?

function Create_Message_Access (Title : String) return Message_Type_Access is
   Object : Message_Type_Access := new Message_Type;
begin
   Object.Title := To_Unbounded_String (Title);

   return Object;
end Create_Message_Access;

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

First_Message := Message.Create_Message ("First");

Ada.Text_IO.Put_Line (First_Message.Get_Title);

First_Message.Update_Title ("First changed");

Ada.Text_IO.Put_Line (First_Message.Get_Title);

First_Message.Update_Title ("First");

Ada.Text_IO.Put_Line (First_Message.Get_Title); 

Вывод (как и ожидалось):

First
First changed
First

Затем я сохранил сообщение в векторном контейнере и попытался изменить заголовок, перебирая элементы, хранящиеся в векторе.Я обнаружил, что заголовок сохраняет старое значение, поэтому я предполагаю, что вызов операции Message_Vector.Element (Message_Cursor) возвращает только копию сообщения, хранящегося в векторе.Правильно или нет?

Messages.Append (First_Message);

Message_Cursor := Message_Vector.First (Messages);

while Message_Vector.Has_Element (Message_Cursor) loop
   declare
      Temporary_Message : Message.Message_Type;
   begin
      Temporary_Message := Message_Vector.Element (Message_Cursor);
      Temporary_Message.Update_Title ("First changed");
   end;

   Message_Vector.Next (Message_Cursor);
end loop;

Message_Cursor := Message_Vector.First (Messages);

while Message_Vector.Has_Element (Message_Cursor) loop
   --
   -- Prints "First" and not "First changed"
   --
   Ada.Text_IO.Put_Line (Message_Vector.Element (Message_Cursor).Get_Title);

   Message_Vector.Next (Message_Cursor);
end loop;

Последний вопрос: в чем разница между Second_Message.all.Get_Title и Second_Message.Get_Title?

Second_Message := Message.Create_Message_Access ("Second");

Ada.Text_IO.Put_Line (Second_Message.all.Get_Title);
Ada.Text_IO.Put_Line (Second_Message.Get_Title);

Полный исходный код:

with Ada.Containers.Vectors;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

with Ada.Text_IO;

procedure Main is

   package Message is

      type Message_Type is tagged private;

      type Message_Type_Access is access Message_Type;

      function Create_Message (Title : String) return Message_Type;

      function Create_Message_Access (Title : String) return Message_Type_Access;

      function Get_Title (Self : Message_Type) return String;

      procedure Update_Title (Self : in out Message_Type; Title : String);

      function "=" (Left, Right : Message_Type) return Boolean;

   private

      type Message_Type is tagged record
         Title : Unbounded_String;
      end record;

   end Message;

   package body Message is

      function Create_Message (Title : String) return Message_Type is
         Object : Message_Type;
      begin
         Object.Title := To_Unbounded_String (Title);

         return Object;
      end Create_Message;

      function Create_Message_Access (Title : String) return Message_Type_Access is
         Object : Message_Type_Access := new Message_Type;
      begin
         Object.Title := To_Unbounded_String (Title);

         return Object;
      end Create_Message_Access;

      function Get_Title (Self : Message_Type) return String is
      begin
         return To_String (Self.Title);
      end Get_Title;

      procedure Update_Title (Self : in out Message_Type; Title : String) is
      begin
         Self.Title := To_Unbounded_String (Title);
      end Update_Title;

      function "=" (Left, Right : Message_Type) return Boolean is
      begin
         return Left.Title = Right.Title;
      end "=";

   end Message;

   package Message_Vector is new Ada.Containers.Vectors (Index_Type => Natural,
      Element_Type => Message.Message_Type, "=" => Message."=");

   package Message_Access_Vector is new Ada.Containers.Vectors (Index_Type => Natural,
      Element_Type => Message.Message_Type_Access, "=" => Message."=");

   Messages       : Message_Vector.Vector;
   Message_Cursor : Message_Vector.Cursor;

   Messages_Access       : Message_Access_Vector.Vector;
   Message_Access_Cursor : Message_Access_Vector.Cursor;

   First_Message  : Message.Message_Type;
   Second_Message : Message.Message_Type_Access;

begin
   First_Message := Message.Create_Message ("First");

   Ada.Text_IO.Put_Line (First_Message.Get_Title);

   First_Message.Update_Title ("First changed");

   Ada.Text_IO.Put_Line (First_Message.Get_Title);

   First_Message.Update_Title ("First");

   Ada.Text_IO.Put_Line (First_Message.Get_Title);

   --

   Ada.Text_IO.New_Line;

   Messages.Append (First_Message);

   Message_Cursor := Message_Vector.First (Messages);

   while Message_Vector.Has_Element (Message_Cursor) loop
      declare
         Temporary_Message : Message.Message_Type;
      begin
         Temporary_Message := Message_Vector.Element (Message_Cursor);
         Temporary_Message.Update_Title ("First changed");
      end;

      Message_Vector.Next (Message_Cursor);
   end loop;

   Message_Cursor := Message_Vector.First (Messages);

   while Message_Vector.Has_Element (Message_Cursor) loop
      --
      -- Prints "First" and not "First changed"
      --
      Ada.Text_IO.Put_Line (Message_Vector.Element (Message_Cursor).Get_Title);

      Message_Vector.Next (Message_Cursor);
   end loop;

   --

   Ada.Text_IO.New_Line;

   Second_Message := Message.Create_Message_Access ("Second");

   Ada.Text_IO.Put_Line (Second_Message.all.Get_Title);
   Ada.Text_IO.Put_Line (Second_Message.Get_Title);

   --

   Ada.Text_IO.New_Line;

   Messages_Access.Append (Second_Message);

   Message_Access_Cursor := Message_Access_Vector.First (Messages_Access);

   while Message_Access_Vector.Has_Element (Message_Access_Cursor) loop
      declare
         Temporary_Message : Message.Message_Type_Access;
      begin
         Temporary_Message := Message_Access_Vector.Element (Message_Access_Cursor);
         Temporary_Message.Update_Title ("Second changed");
      end;

      Message_Access_Vector.Next (Message_Access_Cursor);
   end loop;

   Message_Access_Cursor := Message_Access_Vector.First (Messages_Access);

   while Message_Access_Vector.Has_Element (Message_Access_Cursor) loop
      Ada.Text_IO.Put_Line (Message_Access_Vector.Element (Message_Access_Cursor).Get_Title);

      Message_Access_Vector.Next (Message_Access_Cursor);
   end loop;

end Main;

1 Ответ

5 голосов
/ 27 марта 2019

Две примитивные операции Create_Message и Create_Message_Access могут использоваться для создания конкретных объектов сообщений.Я не совсем уверен, должна ли примитивная операция возвращать тип Message_Type или Message_Type_Access.Какой тип рекомендуется (эффективность?) Или оба решения не оптимальны?

Что лучше всего зависит от обстоятельств.В общем, лучше избегать указателей, но если то, что вы представляете, является объектом с постоянством в вашей системе, вам нужно иметь только одну его копию, как вы обнаружили.Я сделал их ограниченными и использовал контейнеры указателей.

Я думаю, что первым способом объект создается в стеке, а затем копируется после выполнения оператора return, потому что переменная Object выходитобласти видимостиПравильно или неправильно?

Я думаю, что оно будет скопировано во время оператора return, но кроме этого, да (существуют обстоятельства, связанные с ограниченными типами, когда вы можете «построить объект на месте»,но правила изменились между стандартными выпусками языка, поэтому я не уверен, см. AARM 7.6 (17.1) и готовьтесь быть сбитыми с толку).

Я думаю, что во второмспособ создания объекта в куче, а затем указатель копируется после выполнения оператора return, поскольку переменная Object выходит из области видимости.Правильно или нет?

Снова да.

... Я предполагаю, что вызов операции Message_Vector.Element (Message_Cursor) возвращает только копию сообщения, хранящегося ввектор.Правильно или нет?

Да.

Последний вопрос: в чем разница между Second_Message.all.Get_Title и Second_Message.Get_Title?

В этом случае нет.Но если бы процедура была без параметров, вам нужно было бы использовать первую форму, потому что она явно не была бы вызовом подпрограммы.

Операции типа Element действительно возвращают копию.Если вы хотите обновить элемент в контейнере, вы можете взять копию, изменить ее, а затем использовать Replace_Element для перезаписи оригинала.

Вы, вероятно, найдете его менее громоздким (но только простым)использовать Update_Element:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
procedure Updating_Messages is
   type Message is record
      N : Integer := 0;
   end record;
   package Message_Vectors is
     new Ada.Containers.Vectors (Index_Type => Positive,
                                 Element_Type => Message);
   Messages : Message_Vectors.Vector;
begin
   Messages.Append (Message'(N => 21));                                     -- sorry about this, failing of Google syntax highlighting '
   Messages.Append (Message'(N => 42));                                     --'
   for Cursor in Messages.Iterate loop
      declare
         procedure Increment (It : in out Message) is
         begin
            It.N := It.N + 1;
         end Increment;
      begin
         Messages.Update_Element (Cursor, Increment'Access);                --'
      end;
   end loop;
   for M of Messages loop  -- these are copies
      Ada.Text_IO.Put_Line (M.N'Image);                                     --'
   end loop;
end Updating_Messages;
...