Управление динамическими массивами как членами класса в Delphi
При работе с динамическими массивами в Delphi возникают вопросы о том, как происходит их управление, особенно когда они используются в качестве членов класса. Вопрос заключается в следующем: при передаче динамического массива в метод класса как член, происходит ли его копирование или передача по ссылке? Рассмотрим этот вопрос на примере версии Delphi 10.3.3.
В коде представлены два метода: UpdateArray и UpdateArrayWithParam. Оба метода удаляют первый элемент из массива, но ведут себя по-разному. В методе UpdateArray длина массива остаётся неизменной, в то время как в UpdateArrayWithParam длина корректно уменьшается.
interface
type
TSomeRec = record
Name: string;
end;
TSomeRecArray = array of TSomeRec;
TSomeRecUpdate = class
Arr: TSomeRecArray;
procedure UpdateArray;
procedure UpdateArrayWithParam(var ParamArray: TSomeRecArray);
end;
implementation
procedure TSomeRecUpdate.UpdateArray;
begin
Delete(Arr, 0, 1);
end;
procedure TSomeRecUpdate.UpdateArrayWithParam(var ParamArray: TSomeRecArray);
begin
Delete(ParamArray, 0, 1);
end;
procedure Test;
var
r: TSomeRec;
lArr: TSomeRecArray;
recUpdate: TSomeRecUpdate;
begin
lArr := [];
r.Name := 'abc';
lArr := lArr + [r];
r.Name := 'def';
lArr := lArr + [r];
recUpdate := TSomeRecUpdate.Create;
recUpdate.Arr := lArr;
recUpdate.UpdateArray;
// Здесь длина массива не обновляется, остаётся 2.
// ... (дальше код, демонстрирующий корректное уменьшение длины массива в методе UpdateArrayWithParam)
recUpdate.Free;
end;
Подтверждённый ответ:
Динамические массивы в Delphi передаются по ссылке. Они управляются компилятором, который использует систему подсчёта ссылок. Документация по структурированным типам в Delphi описывает это поведение.
Однако, после вызова метода UpdateArray, длина массива lArr остаётся равной 2, что может вызвать недоумение. Это происходит из-за процедуры Delete, которая должна перераспределить динамический массив и, следовательно, изменить указатели на него. Однако, Delete знает только об одном из этих указателей — том, который был передан в функцию.
Похоже на то, что после изменения динамического массива в методе UpdateArray, указатель Arr в экземпляре класса TSomeRecUpdate остаётся связанным с изменённым массивом, в то время как исходный массив lArr, с которого была скопирована ссылка, не обновляется.
Альтернативный ответ:
Это интересный вопрос! Процедура Delete изменяет длину динамического массива, как и функция SetLength, и поэтому должна перераспределить динамический массив. Она также изменяет указатель, переданный ей, на новое место в памяти. Однако, она не может изменить другие указатели на старый динамический массив.
Таким образом, ожидается, что старый динамический массив должен иметь уменьшенный счётчик ссылок и быть создан новый динамический массив со счётчиком ссылок 1. Указатель, переданный в Delete, будет установлен на этот новый динамический массив.
Тем не менее, на практике это не всегда происходит. В коде представлен минимальный пример, демонстрирующий неожиданное поведение. После удаления первого элемента из массива, старый массив остаётся изменённым, что может быть интерпретировано как ошибка.
В коде RTL источника Delete сначала пытается уменьшить размер уже выделенной памяти, но затем вызывает функцию DynArraySetLength, которая может создать новый динамический массив, скопировав старый. В результате, исходный массив остаётся изменённым, и новый динамический массив размещается поверх него.
Вывод:
Использование процедуры Delete для динамических массивов может привести к неожиданным результатам, особенно когда эти массивы являются членами класса. Рекомендуется избегать использования Delete в новых проектах и использовать альтернативные методы управления динамическими массивами, такие как SetLength.
Дополнительно, стоит отметить, что в современном коде, написанном в 2020 году, использование Delete не является лучшей практикой, и лучше позволить этой функции устареть.
При работе с динамическими массивами в качестве членов класса в Delphi, передача массива методу класса происходит по ссылке, но могут возникать сложности с обновлением длины массива после его изменения в методе из-за особенностей работы с подсчетом ссыло
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.