3

据我所知 - 子程序对其父函数/过程具有私有访问模式,对吗?

有什么方法可以从“外部世界”访问它们 - dpr 或单元中的其他功能/程序?

另外-哪种方式需要更多的计算和空间来编译文件?

例如:

function blablabla(parameter : tparameter) : abcde;
 procedure xyz(par_ : tpar_);
 begin
  // ...
 end;
begin
 // ...
end;

procedure albalbalb(param : tparam) : www;
begin
 xyz(par_ : tpar_); // is there any way to make this function public / published to access it therefore enabling to call it this way?
end;

// all text is random.

// also, is there way to call it from DPR in this manner?

// in C++ this can be done by specifing access mode and/or using "Friend" class .. but in DELPHI?
4

4 回答 4

7

嵌套过程/函数 - 在另一个过程或函数中声明的那些是一种特殊类型,因为它们可以访问它们嵌套的过程的堆栈(以及参数/局部变量)。因此,以及 Delphi 范围规则,有没有办法在“父”程序之外访问它们。只有在需要利用它们的特殊功能时才使用它们。AFAIK Delphi/Pascal 是少数具有此功能的语言之一。从编译器的角度来看,调用有一些额外的代码来允许访问父堆栈帧 IIRC。C ++中的AFAIK“朋友”类/函数是不同的-它们是类访问方法,而在您的示例中,您使用的是普通过程/函数。在 Delphi 中,在同一单元中声明的所有过程/类都自动成为“朋友”,声明用于最新的 Delphi 版本。例如,只要所有内容都在同一个单元中,此代码片段就可以工作:

  type
    TExample = class
    private
      procedure HelloWorld;
    public
    ...
    end;

  implementation

    function DoSomething(AExample: TExample);
    begin
      // Calling a private method here works
      AExample.HelloWordl;
    end;
于 2010-05-19T15:09:20.410 回答
5

注意:嵌入式例程 <> 私有/受保护方法。

嵌入式例程,即例程内部的例程不能被外部例程访问。您发布了一个嵌入式例程的示例,我还听说它们称为内部例程。

这是另一个例子:

procedure DoThis;

function DoThat : Boolean;
begin
  // This Routine is embedded or internal routine.
end;
begin

// DoThat() can only be accessed from here no other place.

end;

不管可见性如何,类上的方法都可以通过 RTTI 使用 Delphi 2010 调用。我在本文中详细介绍了如何执行此操作。

如果你在同一个 Unit 类上的方法可以被任何其他代码访问而不管可见性,除非它们被标记为 Strict Private。 这个问题在接受的答案中有更多细节和很好的示例代码。

如果你在两个不同的单位,你可以使用 Protected Method Hack 来访问受保护的方法。这在这篇文章中有详细的介绍。

于 2010-05-19T15:07:13.917 回答
3

是的,您可以从外部世界访问嵌套在其他(父)子程序中的子程序。虽然有点棘手。我在网上找到了这个howto。

如何将嵌套例程作为过程参数传递(32 位)

Delphi 通常不支持将嵌套例程作为过程参数传递:

// This code does not compile:
procedure testpass(p: tprocedure);
begin
  p;
end;
procedure calltestpass;
 procedure inner;
 begin
   showmessage('hello');
 end;
begin
  testpass(inner);
end;

显而易见的解决方法是在 testpass 中传递过程地址并对其进行类型转换:

// This code compiles and runs OK
procedure testpass(p: pointer);
begin
  tProcedure(p);
end;
procedure calltestpass;
 procedure inner;
 begin
   showmessage('hello');
 end;
begin
  testpass(@inner);
end;

然而,在上面的例子中有一个陷阱——如果“内部”例程引用了在从 testpass 调用“内部”过程之前被压入堆栈的任何变量(calltestpass 参数——如果有任何变量,或者calltestpass - 如果有的话),你的系统很可能会崩溃:

// This code compiles OK but generates runtime exception (could even be
//  EMachineHangs :-) )
procedure testpass(p: pointer);
begin
  tProcedure(p);
end;
procedure calltestpass;
var msg: string;
 procedure inner;
 begin
   msg := 'hello';
   showmessage(msg);
 end;
begin
  testpass(@inner);
end;

简单来说,原因是堆栈帧排列被调用 testpass 例程“破坏”了,并且“内部”过程错误地计算了参数和局部变量的位置(请不要责怪 Delphi)。解决方法是在“testpass”中调用“inner”之前设置正确的堆栈上下文。

// This code compiles and runs OK
{$O-}
procedure testpass(p: pointer);
var callersBP: longint;
begin
  asm // get caller's base pointer value at the very beginning
    push dword ptr [ebp]
    pop callersBP
  end;
// here we can have some other OP code
  asm // pushes caller's base pointer value onto stack and calls tProcedure(p)
    push CallersBP
    Call p
    Pop  CallersBP
  end;
// here we can have some other OP code
end;
{$O+}

procedure calltestpass;
var msg: string;
 procedure inner;
 begin
   msg := 'hello';
   showmessage(msg);
 end;
begin
  testpass(@inner);
end;

请注意,对于 testpass 例程,优化已关闭 - 优化通常不能很好地处理混合 OP/汇编代码。

于 2012-09-06T18:46:43.037 回答
2

不,没有办法按照你的要求去做。该xyz函数只能由封闭blablabla函数调用。在该函数之外,xyz不在范围内,也无法命名。如果 C++ 允许嵌套函数,那么也没有任何方法可以引用它,就像无法从当前翻译单元外部引用具有静态链接的函数一样。

如果您需要xyzblablabla函数外部调用,则移到xyz外部。如果您需要从当前单元外部调用它,则需要在单元的接口部分中声明该函数。然后,将该单元添加到外部代码的uses子句中,您可以xyz从任何您想要的地方调用,甚至是 DPR 文件。

如果xyz引用blablabla函数的变量或参数,则需要将它们作为参数传递,xyz否则将无法再访问它们。

访问说明符的概念在这里并不重要,因为我们不是在谈论类。单元具有接口实现部分,它们与类的公共私有部分并不完全相同。

于 2010-05-19T15:03:31.897 回答