Delphi Combining const and out parameters

FireWind

Свой
Регистрация
2 Дек 2005
Сообщения
1,957
Реакции
1,199
Credits
4,009
Combining const and out parameters
March 30, 2021 by Dalija Prasnikar

This is a continuation of a previous blog post, Для просмотра ссылки Войди или Зарегистрируйся Basically, adding an out parameter into the mix does not change anything that has been said in the previous post about using const parameters. But out parameters, with their own side-effects, create a specific situation that requires additional explanation.

First, let's see how an out parameter works.

An out parameter is a var parameter with a twist. Just like a var parameter passes the original variable as a reference (pointer), so does the out parameter, but out also tells the compiler that the original value can be discarded, since it is not for passing any input data to the procedure, and will be only used for passing the result out of the procedure. For managed types, the compiler will clear the original contents of the out parameter at the call site.
Код:
procedure OutInt(out p: integer);
begin
  p := 5;
end;

procedure OutString(out p: string);
begin
  p := '5';
end;

procedure Test;
var
  x: integer;
  s: string;
begin
  x := 2;
  s := '2';

  OutInt(x);
  Writeln(x);
  OutString(s);
  Writeln(s);
end;
5
5

If we remove all code from the procedures, the string parameter will be cleared, but the integer will contain the previously assigned value:
Код:
procedure OutInt(out p: integer);
begin
end;

procedure OutString(out p: string);
begin
end;
2


In combination with a const parameter, out can have unexpected results:
Код:
procedure InOutString(const s: string; out r: string);
begin
  r := s + s;
end;

procedure Test;
var
  s: string;
begin
  s := Copy('abc', 1, 1);
  InOutString(s, s);
  Writeln(s);
end;

Now, one would expect that the resulting string would have the value aa, but instead you will just get an empty string. That is because the output string variable has been cleared before the InOutString procedure is called, and adding an empty string to an empty string will result in an empty string.

Let's remove the const parameter:
Код:
procedure InOutString(s: string; out r: string);
begin
  r := s + s;
end;

If you thought the above code will work, you thought wrong. const is not the culprit here - out is - and replacing the out with var, while keeping const, will give the expected output.
Код:
procedure InOutString(const s: string; var r: string);
begin
  r := s + s;
end;

I am not going to cover other managed types in detail here. You can try it yourself, but don't expect miracles. On the contrary, with interfaces you can fully expect that the whole thing will just blow up right in your face. For instance, using the following procedure and then calling it with the same interface reference for input and output:
Код:
procedure InOutFoo(const f: IFoo; out r: IFoo);
begin
  r := TFoo.Create(f.GetNumber * 2);
end;

So... in a const-out combination, it turns out that const is not the real source of problems. The real question is:

Are out parameters dangerous?​

And the answer is... NO...

Just as with const parameters, the real culprit are not parameter modifiers, but the bad coding practice of passing the same variable as input and output parameters. It is just bad code.

It can be easily broke, it obscures the intent of the code, and it is like drinking from and peeing into the same bottle. Don't do that.
 
Последнее редактирование: