19

const当我在过程中使用参数时,它有什么不同?

以以下过程为例:

procedure DoSomething(Sender: TObject; const Text: String; var Reply: String);
begin
  //Text is read-only and Reply will be passed back wherever DoSomething() was called
  Reply:= Text;
end;

该参数Text: String带有前缀,const以便(据我所知)制作和使用该值的副本 - 并且是只读的。我想知道的是,这对应用程序的影响与我没有放在const那里有何不同?也许是表演技巧?

4

4 回答 4

28

查看文档状态:

“使用 const 允许编译器优化结构化和字符串类型参数的代码。它还提供了一种保护措施,防止无意中通过引用另一个例程来传递参数。”

例如,在字符串的情况下,优化意味着在作为 const 传递时没有额外的引用计数。同样作为 const 传递并不意味着它是一个副本。通常它在内部作为引用传递,因为编译器确保没有对它的写访问。

一些非常有趣的文章可以完全理解幕后发生的事情:

http://delphitools.info/2010/07/28/all-hail-the-const-parameters

http://vcldeveloper.com/articles/different-function-parameter-modifiers-in-delphi

编辑:

一个简单的例子表明 const 可能会导致内部引用传递:

program Project1;

{$APPTYPE CONSOLE}

type
  PMyRecord = ^TMyRecord;
  TMyRecord = record
    Value1: Cardinal;
    Value2: Cardinal;
  end;

procedure PassAsConst(const r: TMyRecord);
begin
  PMyRecord(@r).Value1 := 3333;
  PMyRecord(@r).Value2 := 4444;
end;

procedure PassByVal(r: TMyRecord);
begin
  PMyRecord(@r).Value1 := 3333;
  PMyRecord(@r).Value2 := 4444;
end;

var
  r: TMyRecord;
begin
  r.Value1 := 1111;
  r.Value2 := 2222;
  PassByVal(r);
  Writeln(r.Value1);
  Writeln(r.Value2);

  PassAsConst(r);
  Writeln(r.Value1);
  Writeln(r.Value2);

  Readln;
end.
于 2012-06-12T17:30:24.663 回答
9

当您没有 const 前缀时,编译器必须假定您将更改参数。这意味着复制它并设置一个隐藏的尝试...最终处理本地字符串变量,因此有时 const 可以产生显着的性能改进。它还使生成的代码更小。

于 2012-06-12T17:26:16.933 回答
1

除了前面关于使用 const 时效率的答案(即编译器不需要复制变量)之外,如果您使用带接口参数的 const,它会阻止触发 ref 计数。

于 2012-06-12T18:02:16.550 回答
0

当您const string在函数中使用参数时,您向 Delphi 编译器承诺您不会从中调用任何其他函数,至少在您将所有这些const string参数复制到您将拥有的局部变量之前不会。

https://github.com/the-Arioch/XE2_AutoOpenUnit/blob/master/Delphi_String_Bug/Girli_str_2xFree_Minimized.dpr

https://plus.google.com/+AriochThe/posts/WB3toSpAdfA

program Girli_str_2xFree_Minimized;

{$APPTYPE CONSOLE}

// initial mini-demo by Ãèðëèîíàéëüäî - http://www.sql.ru/forum/memberinfo.aspx?mid=249076
// variance IfDef's re-added by Arioch (orginal reporter)

uses
  SysUtils;

{.$Define TestBug_Impl_Exit}
{.$Define TestBug_Impl_AsIs}

{.$Define NewStr_Unique}

var
  rFile: string; 
// global or local does not matter.
// originally it was an object member.

{$IfNDef TestBug_Impl_Exit} {$IfNDef TestBug_Impl_AsIs}
function TestBUG(const S: string): string;
begin
  Result := S;
end;
{$EndIf}{$EndIf}

{$IfDef TestBug_Impl_AsIs}
procedure TestBUG(const S: string; var Result: string);
begin
  Result := S;
end;
{$EndIf}

{$IfDef TestBug_Impl_Exit}
function TestBUG(const S: string): string;
begin
  Exit(S);
end;
{$EndIf}

procedure Test(const FileName: string);
{$IfDef TestBug_Impl_AsIs} var unnamed_temp: string; {$EndIf}
begin

// rFile := FileName.SubString(0, Length(FileName));  // unavail in XE2

{$IfNDef NewStr_Unique}
  rFile := Copy(FileName, 1, Length(FileName));
  // reference-counting broken, de facto writes into const-string (destroys it)
{$Else}
  rFile := FileName; // no bug, reference-counting proceeded normally!
  UniqueString(rFile);
{$EndIf}

{$IfNDef TestBug_Impl_AsIs}
  TestBUG(FileName); // try to use the const-pointer to the old string
{$Else}
  TestBUG(FileName, unnamed_temp);
{$EndIf}
end; // <== Fatality here

begin
  try
    try
      rFile := ParamStr(0);
      Test(rFile);

      Writeln('Safely returned from the hazardous function without memory dislocations.');

    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;
  finally
    Writeln;
    Writeln('Read the output. Press ENTER to terminate the program.');
    Readln;
  end;
end.

由于 volatile string 和 const string 实际上是两种不同的类型——经典编译器对它们的处理方式不同——应该添加数据转换:当调用 const-string 函数时传递它 volatile-string 参数 Delphi 可以增加使用计数器并减少它函数退出后。将 const-strings 参数向下传递到下一个 const-string 函数就是现在的样子,一旦编译器将 volatile 字符串类型转换为 const 字符串,它就可以保持不变。

可悲的是,事实并非如此。这里是龙。

因此,如果您的函数具有 const 字符串参数 - 要么不要从中调用,要么将这些参数缓存到本地变量中。

于 2018-08-16T19:29:48.370 回答