Delphi передавая параметры по ссылке или по значению / копии - PullRequest
10 голосов
/ 03 марта 2012

Context 1

var text:String;

text:='hello';

myFunc(text);

Context2

function myFunc(mytext:String);
var textcopy:String;
begin

    textcopy:=mytext;

end;

myFunc на Context2 был вызван из Context1, локальная переменная mytext указывает на память вне Context2?или mytext имеют собственную область памяти внутри области действия и заполнены / скопированы с тем же содержимым text?Я, вероятно, упускаю что-то действительно простое, потому что я получаю ошибку access violation.

Есть какой-либо способ явно указать, должна ли функция получать параметры по ссылке или по значению, копируя затем как в C?Я не уверен, как я это делаю.

Ответы [ 3 ]

23 голосов
/ 03 марта 2012

Управление памятью для строк Delphi немного необычно.После того, как вы вызовете myFunc(text) и назначите textcopy := mytext, все три переменные (text, mytext и textcopy) будут указывать на один и тот же адрес, что и исходная строка.

Но как только вы используете одну из этих переменных для внесения изменений в строку, Delphi клонирует строку за кулисами, и ваши изменения применяются к копии.Две другие переменные по-прежнему указывают на оригинал, поэтому они остаются неизменными.Таким образом, любые изменения, сделанные в контексте 2, не будут видны в контексте 1 - эта механика «копирование при записи» эффективно предоставляет семантику передачи по значению.Все эти строки подсчитываются по ссылкам и будут автоматически освобождены после того, как все ссылки выйдут из области видимости.

Однако есть исключение.Если вы получите доступ к строке с помощью указателей, а не строковых операций, вы пропустите шаг копирования, и ваши изменения повлияют на оригинал.Вы также обойдете логику подсчета ссылок и потенциально получите указатель на освобожденный блок памяти.Это может быть причиной вашего нарушения прав доступа, но я не могу сказать без подробностей / большего количества кода.

Если вы хотите передать ссылку, объявите вашу функцию как myFunc(var mytext: String).Если вы хотите заставить Delphi скопировать строку, вместо того, чтобы ждать ее изменения, вы можете использовать System.UniqueString.

13 голосов
/ 03 марта 2012

В Delphi строка - это ссылочный тип, который обычно действует как тип значения.Он размещается в куче (а не в стеке, как большинство типов значений) и обладает автоматическим подсчетом ссылок и семантикой копирования при записи.

Чтобы понять, что это значит, рассмотрим, как нормальные значения типов, например, Integer,ведут себя при передаче в качестве параметра в процедуру:

var
  gValue: Integer;

procedure PassByValue(aValue: Integer);
begin
  // Here @gValue <> @aValue
  aValue := aValue + 2;
  // Here @gValue <> @aValue
end;

procedure PassByRefrenceInOut(var aValue: Integer);
begin
  // Here @gValue = @aValue
  aValue := aValue + 2;
  // Here @gValue = @aValue
end;

procedure CallProcedures;
begin
  gValue := 0; 
  PassByValue(gValue);
  // gValue is still 0
  PassByReferenceInOut(gValue);
  // gValue is 2
end;

Параметр var в PassByReferenceInOut эквивалентен соглашению C о передаче указателя на аргумент.

Та же семантика применима к строкепередача параметров, но есть небольшая разница во внутреннем представлении значений:

var
  gValue: string;

procedure PassByValue(aValue: string);
begin
  // Here PChar(gValue) = PChar(aValue) <<<<
  aValue := aValue + '2';
  // Here PChar(gValue) <> PChar(aValue)
end;

procedure PassByRefrenceInOut(var aValue: string);
begin
  // Here PChar(gValue) = PChar(aValue)
  aValue := aValue + '2';
  // Here PChar(gValue) = PChar(aValue)
end;

procedure CallProcedures;
begin
  gValue := ''; 
  PassByValue(gValue);
  // gValue is still ''
  PassByReferenceInOut(gValue);
  // gValue is '2'
end;

Если вы хотите убедиться, что процедура работает с собственной копией строкового значения, используйте процедуру UniqueString, например:

procedure PassByValue(aValue: string);
begin
  // Here PChar(gValue) = PChar(aValue)
  UniqueString(aValue);
  // Here PChar(gValue) <> PChar(aValue)
  aValue := aValue + '2';
  // Here PChar(gValue) <> PChar(aValue)
end;
9 голосов
/ 03 марта 2012

В Delphi для передачи по ссылке вы явно добавляете ключевое слово var:

procedure myFunc(var mytext:String);

Это означает, что myFunc может изменить содержимое строки и заставить вызывающего пользователя просмотреть изменения.

...