Справочник Ada 95
7.6 Пользовательское определение присваивания и финализации
Три типа действий являются фундаментальными для работы с объектами: инициализация, финализация и присваивание. Каждый объект инициализируется, явным или по умолчанию, после создания (например, с помощью object_declaration или allocator). Каждый объект финализируется перед уничтожением (например, при выходе из subprogram_body содержащего object_declaration, или с помощью вызова экземпляра Unchecked_Deallocation). Операция присваивания используется в assignment_statement, явной инициализации, передаче параметров и других операциях.
Язык предоставляет стандартные определения этих трех основных операций, но управляемый тип предоставляет пользователю дополнительный контроль над этими операциями. В частности, пользователь может определить для управляемого типа процедуру Initialize, которая вызывается сразу после стандартной инициализации управляемого объекта, процедуру Finalize, которая вызывается непосредственно перед финализацией любого из компонентов управляемого объекта, и процедуру Adjust, которая вызывается как последний шаг присваивания нелимитированному управляемому объекту.
Статическая семантика
Существует следующий пакет библиотеки, определенный языком:
package Ada.Finalization is
pragma Preelaborate(Finalization);
pragma Remote_Types(Finalization);
pragma Preelaborate(Finalization);
pragma Remote_Types(Finalization);
type Controlled is abstract tagged private;
procedure Initialize (Object : in out Controlled);
procedure Adjust (Object : in out Controlled);
procedure Finalize (Object : in out Controlled);
procedure Adjust (Object : in out Controlled);
procedure Finalize (Object : in out Controlled);
type Limited_Controlled is abstract tagged limited private;
procedure Initialize (Object : in out Limited_Controlled);
procedure Finalize (Object : in out Limited_Controlled);
private
... -- не определено языком
end Ada.Finalization;
procedure Finalize (Object : in out Limited_Controlled);
private
... -- не определено языком
end Ada.Finalization;
Управляемый тип является потомком Controlled или Limited_Controlled. (Стандартные) реализации Initialize, Adjust и Finalize не оказывают никакого влияния. Предустановленный оператор "=" типа Controlled всегда возвращает True, поскольку этот оператор включен в реализацию предустановленного оператора равенства для типов, полученных из Controlled, как объяснено в 4.5.2. Тип Limited_Controlled подобен Controlled, за исключением того, что он ограничен и лишен примитивной подпрограммы Adjust.
Динамическая семантика
Во время обработки object_declaration для каждого управляемого подкомпонента объекта, которому не присвоено начальное значение (как определено в 3.3.1), вызывается Initialize для этого подкомпонента. Аналогично, если объект в целом управляемый и ему не присвоено начальное значение, то вызывается Initialize для объекта. То же самое относится к оценке allocator, как объясняется в 4.8.
Для extension_aggregate, у которого ancestor_part является subtype_mark, для каждого управляемого подкомпонента части предка либо вызывается Initialize, либо присваивается его начальное значение, в зависимости от ситуации; если тип части предка сам по себе управляемый, вызывается процедура Initialize типа предка, если эта процедура не абстрактная.
Initialize и другие операции инициализации выполняются в произвольном порядке, за исключением следующих случаев. Initialize применяется к объекту после инициализации его подкомпонентов, если таковые имеются (включая неявную инициализацию и вызовы Initialize). Если у объекта есть компонент с дискриминантом доступа, ограниченным выражением для каждого объекта, Initialize применяется к этому компоненту после любых компонентов, не имеющих таких дискриминантов. Для объекта с несколькими компонентами с таким дискриминантом Initialize применяется к ним в порядке их component_declarations. Для allocator, любые активации задач следуют за всеми вызовами Initialize.
При присваивании значения объекту-цели с любыми управляемыми частями, либо при его создании, либо в последующем assignment_statement, операция присваивания выполняется следующим образом:
- Значение целевого объекта становится присвоенным значением.
- Значение целевого объекта корректируется.
Для корректировки значения составного объекта (нелимитированного), значения компонентов объекта сначала корректируются в произвольном порядке, а затем, если объект управляемый, вызывается Adjust. Корректировка значения элементарного объекта не оказывает влияния, также как и корректировка значения составного объекта без управляемых частей.
Для assignment_statement, после того как name и expression были оценены, а также выполнены все преобразования (включая проверку ограничений), создается анонимный объект, и значение присваивается в него; то есть применяется операция присваивания. (Присваивание включает корректировку значения.) Затем целевой объект assignment_statement финализируется. Значение анонимного объекта затем присваивается целевому объекту assignment_statement. Наконец, анонимный объект финализируется. Как объяснено ниже, реализация может исключить промежуточный анонимный объект, поэтому это описание включает в себя то, что дано в 5.2, «Операции присваивания».
Требования к реализации
Для aggregate управляемого типа, значение которого присваивается, кроме как с помощью assignment_statement или return_statement, реализация не должна создавать отдельный анонимный объект для aggregate. Значение агрегата должно быть построено непосредственно в целевом объекте присваивания, и Adjust не вызывается для целевого объекта.
Разрешения реализации
Реализация имеет право смягчить вышеупомянутые правила (для нелимитированных управляемых типов) следующими способами:
- Для assignment_statement, который присваивает объекту значение этого же объекта, реализация может ничего не делать.
- Для assignment_statement для не управляемого типа реализация может финализировать и присваивать каждый компонент переменной по отдельности (вместо финализации всей переменной и присваивания всего нового значения), если дискриминанта переменной не меняется при присваивании.
- Для aggregate или вызова функции, значение которого присваивается целевому объекту, реализация не обязана создавать отдельный анонимный объект, если она может безопасно создать значение aggregate или вызова функции непосредственно в целевом объекте. Аналогично, для assignment_statement реализация не обязана создавать анонимный объект, если присваиваемое значение является результатом оценки name, обозначающего объект (исходный объект), чья память не может пересекаться с целевым. Если исходный объект может пересекаться с целевым, то реализация может избежать необходимости в промежуточном анонимном объекте, используя одно из вышеуказанных разрешений и выполняя присваивание по одному компоненту за раз (для присваивания массива, где значения перекрываются), или вообще не выполнять его (для присваивания, где целевой и исходный объекты присваивания являются одним и тем же объектом). Даже если создается анонимный объект, реализация может переместить его значение в целевой объект как часть присваивания, не перенастраивая его, пока у анонимного объекта нет связанных подкомпонентов.