Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 当我使用“时,有什么区别?”;常数;在程序中';s参数?_Delphi_Parameters_Constants - Fatal编程技术网

Delphi 当我使用“时,有什么区别?”;常数;在程序中';s参数?

Delphi 当我使用“时,有什么区别?”;常数;在程序中';s参数?,delphi,parameters,constants,Delphi,Parameters,Constants,在过程中使用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
参数有什么区别

以以下程序为例:

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
放在那里,这对应用程序的影响会有什么不同?也许是性能技巧?

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

查看状态:

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

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

一些非常有趣的文章可以完全理解引擎盖下发生的事情:

编辑:

一个简单的示例显示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.

除了前面关于使用常量时效率的回答(即编译器不需要复制变量),如果使用带有接口参数的常量,则会防止触发ref计数

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

程序Girli\u str\u 2xFree\u最小化;
{$APPTYPE控制台}
//最初的迷你演示http://www.sql.ru/forum/memberinfo.aspx?mid=249076
//Arioch(原始报告者)重新添加的差异IfDef
使用
SysUtils;
{.$Define TestBug\u Impl\u Exit}
{.$Define TestBug\u Impl\u AsIs}
{.$Define NewStr_Unique}
变量
rFile:字符串;
//全球或本地并不重要。
//最初它是一个对象成员。
{$IfNDef TestBug\u Impl\u Exit}{$IfNDef TestBug\u Impl\u AsIs}
函数TestBUG(const S:string):string;
开始
结果:=S;
结束;
{$EndIf}{$EndIf}
{$IfDef TestBug_Impl_AsIs}
过程测试错误(常量S:string;变量结果:string);
开始
结果:=S;
结束;
{$EndIf}
{$IfDef TestBug_Impl_Exit}
函数TestBUG(const S:string):string;
开始
出口;;
结束;
{$EndIf}
程序测试(常量文件名:字符串);
{$IfDef TestBug_Impl_AsIs}var unnamed_temp:string;{$EndIf}
开始
//rFile:=FileName.SubString(0,长度(文件名));//XE2中不可用
{$IfNDef NewStr_Unique}
rFile:=副本(文件名,1,长度(文件名));
//引用计数已中断,事实上写入常量字符串(将其销毁)
{$Else}
rFile:=文件名;//没有错误,参考计数正常进行!
唯一字符串(rFile);
{$EndIf}
{$IfNDef TestBug\u Impl\u AsIs}
TestBUG(文件名);//尝试使用指向旧字符串的常量指针
{$Else}
TestBUG(文件名,未命名的临时文件);
{$EndIf}

完回答这里?在大多数情况下,我认为谨慎地将所有仅输入参数标记为
const
的主要好处是,程序员将获得额外的帮助,以避免愚蠢的错误。@Andreas令人恼火的是,
const
需要包含在接口和实现中。这是一个C++击败Delphi的领域。@安达瑞德布兰德虽然是一个更固执己见的评论,但我却发现这是最合理的答案。很有意义,它实际上只是将其设置为只读,这样无论是谁实现该过程都无法尝试分配该值。然而,与此同时,我看到人们使用没有任何前缀的参数,并临时为其分配一个新值,因为他们知道新值不会像传递
var
参数一样被传回。@Jerrydoge我刚刚在我的答案中添加了一个示例,向您展示了成功分配const参数的尝试;)事实上,它更多地证明了编译器在确保“正常”赋值不起作用的情况下在内部创建了一个pass-by引用。我应该补充一点,这在我安装的最新版本Delphi 2007中是正确的。我认为更高级的编译器可能会检测到一个参数实际上没有被用作变量,即使没有常量前缀,也不会复制它,但这些类型的优化历来不是Delphi研发团队关注的焦点。在所有知道常量参数的Delphi版本中都是如此,也就是说,早在D2007之前:如果结构通过引用传递(这取决于它们的大小),则不需要将其复制到本地存储,也不需要进行引用计数。这就是这种行为的原因:接口和字符串缺少引用计数也是一样的。您所说的关于接口的内容并不是关于字符串的“补充”。变量总是被复制的,但复制这些变量的成本很低,因为它只是复制指针的值,有时只不过是将一个值从一个寄存器复制到另一个寄存器。FWIW:对于它的传递方式,参数是否为常量并不重要。如果未明确声明
var
out
,则会将其传递给常量或非常量。通常,大于寄存器大小(例如,32位)的项将通过引用传递,无论它们是否为常量。常量和非常量之间的唯一区别是对于非常量,隐藏代码
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.