Delphi paramètres de passage par référence ou par valeur / copie

Contexte 1

var text:String;

text:='hello';

myFunc(text);

Contexte2

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

    textcopy:=mytext;

end;

myFunc sur le Contexte2 a été appelée à partir de la Contexte1, la variable locale mytext est-ce que pointer vers un souvenir en dehors du contexte 2? ou mytext avoir leur propre espace mémoire à l'intérieur de la portée, et rempli/copié avec le même contenu de l' <!--5? Je suis probablement en train de rater quelque chose de vraiment basique, parce que je reçois un access violation erreur.

il y a un moyen de spécifier explicitement si une fonction devrait recevoir des paramètres par référence ou par valeur, copier puis comme en C? Je ne suis pas sûr de savoir comment je le fais.

8
demandé sur Vitim.us 2012-03-03 08:33:09

3 réponses

la gestion de la mémoire pour les chaînes Delphi est un peu inhabituelle. Après l'appel de myFunc(text), et d'attribuer textcopy := mytext, les trois variables (text, mytext et textcopy) sera pointant vers la même adresse, celle de la chaîne d'origine.

Mais dès que vous utilisez l'une de ces variables pour modifier la chaîne, Delphi clone la chaîne derrière les scènes, et vos modifications sont appliquées à la copie. Les deux autres variables pointent toujours vers l'original, restent inchangés. Ainsi, tout changement apporté dans le Contexte 2 ne sera pas vu dans le Contexte 1 - ce" copy-on-write " mécanique donne effectivement vous pass-by-value sémantique. Toutes ces chaînes sont comptées par référence, et seront libérées automatiquement une fois que toutes les références seront hors champ.

cependant, il y a une exception. Si vous accédez à la chaîne en utilisant des pointeurs, plutôt que des opérations de chaîne, vous contournerez l'étape de copie et vos modifications affecteront l'original. Vous contournerez également le logique de comptage de référence, et peut-être finir avec un pointeur vers un bloc de mémoire désactivé. C'est peut-être la raison de votre violation de l'accès, mais je ne pourrais pas le dire sans plus de détails / plus de code.

si vous voulez passer de référence, déclarez votre fonction comme myFunc(var mytext: String). Si vous voulez forcer Delphi à copier la chaîne, au lieu d'attendre qu'elle soit modifiée, vous pouvez utiliser System.UniqueString.

20
répondu Nick Barnes 2012-03-03 15:53:37

dans Delphi, string est un type de référence qui agit normalement comme un type de valeur. Il est alloué sur le tas (pas la pile comme la plupart des types de valeur) et dispose d'un comptage de référence automatique et d'une sémantique de copie à l'écriture.

pour comprendre ce que cela signifie, considérons comment les types de valeurs normales, par exemple un entier, se comportent lorsqu'ils sont passés comme paramètre à une procédure:

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;

le paramètre var dans PassByReferenceInOut est équivalent à la convention C de passer un pointeur à argument.

la même sémantique s'applique au passage du paramètre string, mais il y a une différence subtile dans la représentation interne des valeurs:

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;

si vous voulez vous assurer qu'une procédure fonctionne sur sa propre copie de la valeur string, utilisez la procédure UniqueString, par exemple:

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;
12
répondu Henrick Hellström 2012-03-03 12:34:48

dans Delphi pour passer par référence, vous ajoutez explicitement le mot-clé var:

procedure myFunc(var mytext:String);

cela signifie que myFunc peut modifier le contenu de la chaîne et faire voir les changements à l'appelant.

7
répondu Mike W 2012-03-03 05:31:53