#ada
#ada
Вопрос:
Мой вопрос содержит более длинный фрагмент кода Ada. В основном я пытался смоделировать сообщение с заголовком объектно-ориентированным способом. Две примитивные операции 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:
Две примитивные операции Create_Message и Create_Message_Access могут быть использованы для создания конкретных объектов сообщений. Я не совсем уверен, должна ли примитивная операция возвращать тип Message_Type или Message_Type_Access. Какой тип рекомендуется (эффективность?) или оба решения не являются оптимальными?
Что лучше, зависит от обстоятельств. В общем, лучше избегать указателей, но если то, что вы представляете, является постоянным объектом в вашей системе, вам нужно иметь только одну его копию, как вы обнаружили. Я сделал их ограниченными и использовал контейнеры указателей.
Я думаю, что первым способом объект создается в стеке, а затем копируется после выполнения инструкции return, потому что переменный объект выходит за пределы области видимости. Правильно или неправильно?
Я думаю, это было бы скопировано во время инструкции return, но помимо этого, да (есть обстоятельства, связанные с ограниченными типами, когда вы можете «создать объект на месте», но правила изменились между стандартными выпусками языка, поэтому я не уверен; смотрите AARM 7.6 (17.1) и приготовьтесь к недоумению).
Я думаю, что вторым способом объект создается в куче, а затем указатель копируется после выполнения инструкции return, потому что переменный объект выходит за пределы области видимости. Правильно или неправильно?
Опять же, да.
… Я предполагаю вызов операции Message_Vector.Элемент (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;
Комментарии:
1. Если я решу использовать только указатели:
type Message_Type (<>) is tagged limited private;
Было бы полезным определением для типа?2. Да, это то, что я сделал. Конечно (!), в конечном итоге вам приходится каким-то образом управлять освобождением (возможно, через
Ada.Finalization
).3. Если я объявляю тип для своих сообщений как
type Message_Type (<>) is tagged limited
: Давайте представим, что я не заинтересован в предоставленииCreate
функции возвращаемого типаCreate_Message_Access
. Может ли процедура в другом пакете принять типMessage_Type
в качестве параметра, а затем создать копию этого сообщения в переменную сaccess
типом, чтобы скопированное сообщение могло быть сохранено в vector (Message_Access_Vector
)?4. Если вы хотите скопировать сообщение, вы можете либо не создавать его,
limited
либо предоставитьCopy
подпрограмму в первом пакете (тогда это было бы другое сообщение с тем же содержимым, предположительно; это может быть нецелесообразно в вашей системе).