В качестве дополнения к ответам, данным Джимом Роджерсом и Саймоном Райтом, если вы используете Ada 2012, вы можете рассмотреть возможность использования неопределенного держателя для создания массива (см. Также RM A.18.18 и Обоснование Ada 2012, раздел 8.5 )
Как указано в обосновании, держатель - это контейнер, который может содержать (и управлять) один экземпляр объекта. Объект, передаваемый в качестве аргумента подпрограмме To_Holder
(см. Пример ниже), копируется в экземпляр кучи, который, в свою очередь, уничтожается, когда он больше не нужен (например, когда он заменяется или когда держатель выходит из -объем). Следовательно, контейнер-держатель освобождает вас от ручного управления памятью, как если бы вы использовали типы access
напрямую.
Стоимость (производительность) заключается в том, что объект, переданный в программу To_Holder
, копируется. Вы можете «перемещать» объекты между держателями (используя подпрограмму Move
, определенную в пакете держателей), но вы не можете «перемещать» объект в держатель; Вы можете только скопировать его в держатель.
token.ads (spec)
package Tokens is
-- Abstract Root Type.
type Token is abstract tagged private;
function Root_Of_Tree (T : Token) return Integer is abstract;
-- First derived type.
type OToken is new Token with private;
function Root_Of_Tree (T : OToken) return Integer;
function Create_OToken (X, Y : Integer) return OToken;
-- Second derived type.
type VToken is new Token with private;
function Root_Of_Tree (T : VToken) return Integer;
function Create_VToken (X, Z : Integer) return VToken;
private
type Token is abstract tagged record
X : Integer;
end record;
type OToken is new Token with record
Y : Integer;
end record;
type VToken is new Token with record
Z : Integer;
end record;
end Tokens;
tokens.adb (тело)
package body Tokens is
function Root_Of_Tree (T : OToken) return Integer is
begin
return T.X + 2 * T.Y;
end Root_Of_Tree;
function Create_OToken (X, Y : Integer) return OToken is
begin
return OToken'(X, Y);
end Create_OToken;
function Root_Of_Tree (T : VToken) return Integer is
begin
return T.X + 3 * T.Z;
end Root_Of_Tree;
function Create_VToken (X, Z : Integer) return VToken is
begin
return VToken'(X, Z);
end Create_VToken;
end Tokens;
main.adb
with Ada.Text_IO; use Ada.Text_IO;
with Tokens; use Tokens;
with Ada.Containers.Indefinite_Holders;
procedure Main is
package Token_Holder is
new Ada.Containers.Indefinite_Holders (Token'Class);
use Token_Holder;
type Token_Array is array (Integer range <>) of Holder;
Tokens : Token_Array :=
(To_Holder (Create_OToken (1, 2)),
To_Holder (Create_OToken (5, 4)),
To_Holder (Create_VToken (1, 2)),
To_Holder (Create_VToken (5, 4)));
begin
for T of Tokens loop
Put_Line (Integer'Image (T.Element.Root_Of_Tree));
end loop;
end Main;
Запуск valgrind
показывает, что после завершения программы память не выделяется:
$ valgrind ./main
==1392== Memcheck, a memory error detector
==1392== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1392== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==1392== Command: ./main
==1392==
5
13
7
17
==1392==
==1392== HEAP SUMMARY:
==1392== in use at exit: 0 bytes in 0 blocks
==1392== total heap usage: 8 allocs, 8 frees, 160 bytes allocated
==1392==
==1392== All heap blocks were freed -- no leaks are possible
==1392==
==1392== For counts of detected and suppressed errors, rerun with: -v
==1392== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Примечание [обновлено]: существует 8 распределений, в то время как массив содержит только 4 элемента / держателя. Это связано с тем, как держатель реализован для платформ, которые поддерживают атомные приращения / убывания (например, Linux). Для этих платформ реализация создает внутри себя другого «общего держателя», который поддерживает стратегию копирования при записи (см. source ). Для платформ, которые не поддерживают атомарные приращения / убывания, реализация будет более простой (см. source ) и будут показаны только 4 выделения.