Ссылочные типы (указатели)
Введение
Указатели - потенциально опасная конструкция, которая вступает в противоречие с основополагающей философией Ады.
Есть два способа, которыми Ада помогает оградить программистов от опасности указателей:
Один из подходов, который мы уже видели, заключается в обеспечении альтернативных возможностей, чтобы программисту не нужно было использовать указатели. Виды параметров, массивы и типы произвольных размеров - это все конструкции, которые могут заменить типичное использование указателей в C.
Во-вторых, Ада сделала указатели максимально безопасными и ограниченными, хотя допускает «аварийные люки», которые программист явно затребовать и, предположительно, будет использовать их с соответствующей осторожностью.
Вот как в Аде объявляется простой тип указателя, ссылочный тип:
На этом примере показано, как:
Объявить ссылочный тип, значения которого указывают на объекты определенного типа
Объявить переменную (для ссылочных значений) этого ссылочного типа
Присвоить ему значение
null
В соответствии с философией строгой типизации Ада, если объявить второй ссылочный тип, указывающий на Date, эти два ссылочных типа будут несовместимы друг с другом:
На других языках
Большинство других языков используют структурно типизацию для указателей, что означает, что два типа указателей считаются одинаковыми, если они имеют один и тот же целевой тип.
В Аде это не так, и к этому, возможно, придется какое-то время привыкать. Казалось бы, простой вопрос, если вы хотите для вашего типа иметь канонический ссылочный тип, где его объявить? Обычно используют следующий подход, если понадобится ссылочный тип для некоторого вашего типа, то вы объявите его вместе с типом:
package Access_Types is
type Point is record
X, Y : Natural;
end record;
type Point_Access is access Point;
end Access_Types;
Выделение (allocation) памяти
После того, как мы объявили ссылочный тип, нам нужен способ присвоить
переменным этого типа осмысленные значения! Вы можете получить
значение ссылочного типа с помощью ключевого слова new
.
Если тип, память для значение которого требуется выделить, требует ограничений, их можно указать после подтипа, как в объявлении переменной:
Однако, в некоторых случаях выделение памяти путем указания типа не является идеальным, поэтому Ада позволяет инициализировать объект одновременно с выделением памяти. Чтобы сделать это необходимо использовать квалифицированное выражение:
Извлечение по ссылке
Последняя часть мозайки ссылочных типов языка Ада покажет нам, как получить
значение объекту по ссылке, то есть как «разыменовать» указатель.
Для этого в Аде используется синтаксис .all
, но часто в нем
вообще нет необходимости - во многих случаях использования ссылочного значения
эта операция выполняется неявно:
Другие особенности
Как вы, возможно, заметили, если пользовались указатели в C или C++, мы не показали некоторых функций, которые считаются основополагающими при использования указателей, таких как:
Арифметика над указателями (возможность увеличивать или уменьшать указатель, чтобы переместить его на следующий или предыдущий объект)
Освобождение памяти вручную - то, что в C делается с помощью
free
илиdelete
. Это потенциально опасная операция. Чтобы оставаться в безопасном пространстве Ады, вам лучше не освобождать память вручную.
Эти функции существуют в Аде, но воспользоваться ими можно только с помощью определенных интерфейсов стандартной библиотеки (API).
Attention
Общепринятый принцип языка гласит, что в большенстве случаев вы можете избегать ручного управления памятью, и вам лучше следовать ему.
Существует множество способов избежать распределения памяти вручную, с некоторыми из них мы уже встречались (например, виды параметров). Язык также предоставляет библиотечные абстракции, чтобы избежать указателей:
Одним из них является использование контейнеров. Контейнеры помогают пользователям избегать указателей, поскольку сами управляют памятью.
Контейнер, который следует отметить в этом контексте, является Indefinite holder. Этот контейнер позволяет хранить значение неопределенного типа, например String.
GNATCOLL🤢 имеет библиотеку для интеллектуальных указателей, называемую Refcount, память этих указателей автоматически управляется, так что, когда у выделенного объекта больше нет ссылок на него, память автоматически освобождается.
Взаимно рекурсивные типы
Связанный список является широко известной идиомой в программировании; в Аде его наиболее естественная запись включает определение двух типов - тип записи и ссфлочный тип, которые будут взаимно зависимыми. Для объявления взаимно зависимых типов можно использовать неполное объявление типа: