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 スコープの規則により、 「親」プロシージャの外でそれらにアクセスする方法はありません。それらの特別な機能を利用する必要がある場合にのみ使用してください。私の知る限り、Delphi/Pascal は、この機能を持つ数少ない言語の 1 つです。コンパイラの観点から見ると、この呼び出しには、親スタック フレームである 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;

クラスのメソッドは、可視性に関係なく、Delphi 2010 を使用して RTTI 経由で呼び出すことができます。この記事でこれを行う方法を詳しく説明しました。

クラスの同じ Unit メソッドにいる場合、Strict Private でマークされていない限り、可視性に関係なく他のコードからアクセスできます。 この質問には、受け入れられた回答に詳細と良いサンプル コードがあります。

2 つの異なるユニットにいる場合は、Protected Method Hack を使用して保護されたメソッドにアクセスできます。これについては、この記事で詳しく説明しています。

于 2010-05-19T15:07:13.917 に答える
3

はい、他の (親) サブルーチンにネストされたサブルーチンに、外界からアクセスできます。少しトリッキーですが。このハウツーをウェブで見つけました。

ネストされたルーチンを手続き型パラメーターとして渡す方法 (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++ がネストされた関数を許可する場合、現在の翻訳単位の外から静的リンケージを持つ関数を参照する方法がないのと同様に、それを参照する方法もありません。

xyz関数の外側から呼び出す必要がある場合は、外側blablablaに移動xyzします。現在のユニットの外部から呼び出す必要がある場合は、ユニットのインターフェイス セクションでその関数を宣言する必要があります。次に、そのユニットを外部コードのuses句に追加するとxyz、DPR ファイルからでも、好きな場所から呼び出すことができます。

xyz関数の変数またはパラメーターを参照する場合はblablabla、それらをパラメーターとして渡す必要がありますxyz。それ以外の場合はアクセスできなくなるためです。

クラスについて話しているわけではないので、アクセス指定子の概念はここではあまり関係ありません。ユニットにはインターフェイスセクションと実装セクションがあり、クラスのパブリックセクションやプライベートセクションと実際には同じではありません。

于 2010-05-19T15:03:31.897 に答える