1

オブジェクトの「Self」変数を扱う多くの質問とリソースを見てきましたが、誰もが何か違うことを言っています。

たとえば、この質問では:Delphiセルフポインタの使用法

質問に対する最高評価の回答は間違っているようです。Pointer(Self)は、それを含むオブジェクトを指していません。また、オブジェクト内から参照を渡すために使用することはできません。

私はこれをやってみました:

Type
  myobject = class
    PSelf: Pointer;
  End;

Var
  Obj: myobject;

Procedure RunProgram;
Begin
  Obj := myobject.create;
  Obj.PSelf := @Obj;

  //Run the rest of the program
  .
  .
  .

ほとんどの場合、これは問題なく機能しました。

私の質問は:これは良いコーディング方法ですか?「PSelf」変数は、プログラムの実行中にオブジェクトを指すことが期待できますか?

最近、「PSelf」がオブジェクトを含むオブジェクトを指すのをやめたというバグに遭遇しました。オブジェクトがヒープ内でシャッフルされるのか、メモリが破損しているのか疑問に思います。

編集:

「Self」変数の使用がうまくいかなかった場合があり、今ではそれを複製できません。したがって、「PSelf」変数を使用する私の手法と同様に、この質問全体は無意味です。申し訳ありません。

そしてケンが指摘したように、上のリンクには正しい答えがあります:)

4

2 に答える 2

14

SelfDelphiでオブジェクト参照がどのように機能するのか、そしてどのように機能するのかを誤解していると思います。

クラスのインスタンスを含む変数は、すでにそのオブジェクトインスタンスへのポインタです。Delphiコンパイラでは、便宜上、間接参照演算子(^)を省略できます。

var
  MyObject: TMyObject;  
begin
  MyObject := TMyObject.Create;  // MyObject is now a pointer to an instance of TMyObject
  ...

Delphiでは、オブジェクトインスタンスのメンバーまたはプロパティにアクセスするときに、逆参照演算子を使用しないという省略形も使用できます。繰り返しますが、次のコードは実際には同等です。

MyObj.SomeProperty := SomeValue;
MyObj^.SomeProperty := SomeValue;

Delphiのドキュメントから:

クラス型の変数は、実際にはオブジェクトを参照するポインターです。したがって、複数の変数が同じオブジェクトを参照できます。他のポインターと同様に、クラス型変数は値nilを保持できます。ただし、それが指すオブジェクトにアクセスするために、クラス型変数を明示的に逆参照する必要はありません。たとえば、SomeObject.Size:= 100は、SomeObjectによって参照されるオブジェクトのSizeプロパティに値100を割り当てます。これをSomeObject^.Size:=100とは書きません。

Selfオブジェクトの現在のインスタンスを指す、自動的に宣言されたプロパティです。つまり、オブジェクトの現在のインスタンスを参照するためにそのクラスを実装するコード内で自動的に利用可能になります。これにより、同じオブジェクトの複数のインスタンスを持つことができます。

type
  TMyObject=class(TObject)
  private
    FMyInteger: Integer;
    function GetMyInteger: Integer;
    procedure SetMyInteger(Value: Integer);
  published
    property MyInteger: Integer read GetMyInteger write SetMyInteger;
  end;

...
function TMyObject.GetMyInteger: Integer;
begin
  Result := Self.FMyInteger; 

  // Self is implied, so the above line can more simply be written as
  // Result := FMyInteger;
end;

procedure TMyObject.SetMyInteger(Value: Integer);
begin
  if (Value <> Self.FMyInteger) then  // Self is again implied here
    Self.FMyInteger := Value;
end;

var
 MyObjOne, MyObjTwo: TMyObject;
 i, j: Integer;
begin
  MyObjOne := TMyObject;
  // Here, the code inside TMyObject.SetInteger that
  // uses `Self` would refer to `MyObjOne`
  MyObjOne.MyInteger := 1; 

  MyObjTwo := TMyObject;
  // Here, the code in TMyObject.SetInteger would be
  // using the memory in `MyObjTwo` when using `Self`
  MyObjTwo.MyInteger := 2; 
end;        

これは、クラスを実装するコードでSelfのみ有効であることに注意してください。これは、上記で使用可能で有効であり(私の例では実装されている唯一のコード)、常に現在のインスタンスを参照します。TMyObject.GetMyIntegerTMyObject.SetMyInteger

Selfそのオブジェクトインスタンスを参照する変数 Selfそのオブジェクトインスタンスのメソッド内にあるため、のアドレスを追跡する必要はありません。オブジェクトのそのインスタンス内でのみ有効であり、常にそのオブジェクトインスタンスを参照します。したがって、コード例でPSelfは、スペースが無駄になっています-myobjectすでにそれ自体へのポインターが含まれており、そのポインターは次のメソッドで自動的に使用可能になりますmyobject

type
  myobject = class;   // Now automatically contains a `Self` pointer
                      // available in methods of the class once an
                      // instance is created

var
  myobj: myobject;
begin
  myobj := myobject.Create;  // myobj.Self now is valid in methods of
                             // `myobject`
于 2012-12-25T03:20:44.943 に答える
1

これが問題に対する私の解決策です。なぜ2番目の例が機能しないのか疑問に思っています。

これは機能します(しかし、それを行うには間違った方法です):

Type
  myobject1 = class(TObject)
    PSelf: Pointer;
    Number: Integer;
    Function GiveReference: Pointer;
  End;
  pmyobject1: ^myobject1;

  myobject2 = class(TObject)
    p: pmyobject1;
  End;

Var
  Obj1: myobject1;
  Obj2: myobject2;

Function  myobject1.GiveReference: Pointer;
Begin
  Result := PSelf;
End;

Procedure RunProgram;
Var
  i: Integer;
Begin
  Obj1 := myobject1.create;
  Obj1.PSelf := @Obj1;

  Obj2 := myobject2.create;
  Obj2.P := Obj.GiveReference;

  //to access 'number', this works
  i := Obj2.P^.Number;

  //Run the rest of the program
  .
  .
  .

これは機能しませんが、私の考えではまったく同じです。これが私に'self'変数を信用させなかった理由です(そうですが、私はポインターへのポインターを作成していました)。

Type
  myobject1 = class(TObject)
    Number: Integer;
    Function GiveReference: Pointer;
  End;
  pmyobject1: ^myobject1;

  myobject2 = class(TObject)
    p: pmyobject1;
  End;

Var
  Obj1: myobject1;
  Obj2: myobject2;

Function  myobject1.GiveReference: Pointer;
Begin
  Result := @Self;
End;

Procedure RunProgram;
Var
  i: Integer;
Begin
  Obj1 := myobject1.create;

  Obj2 := myobject2.create;
  Obj2.P := Obj.GiveReference;

  //This will fail, some of the time, but not all of the time.
  //The pointer was valid while 'GiveReference' was being called, but
  //afterwards, it is not valid.
  i := Obj2.P^.Number;

  //Run the rest of the program
  .
  .
  .

そして最後に、これは私がずっとやっていたはずのことです:

Type
  myobject1 = class(TObject)
    Number: Integer;
    Function GiveReference: Pointer;
  End;

  myobject2 = class(TObject)
    p: myobject1;
  End;

Var
  Obj1: myobject1;
  Obj2: myobject2;

Function  myobject1.GiveReference: Pointer;
Begin
  Result := Self;
End;

Procedure RunProgram;
Var
  i: Integer;
Begin
  Obj1 := myobject1.create;

  Obj2 := myobject2.create;
  Obj2.P := Obj.GiveReference;

  //No problems with this, although I would generally check if P was assigned prior to
  //reading from it.
  i := Obj2.P.Number;

  //Run the rest of the program
  Obj1.Free;
  Obj2.P := nil;

ポインタを使わないことで、実際にオブジェクト全体を複製しているのではないかと心配していたので、そもそもこれをしていませんでした。

于 2012-12-25T21:13:20.363 に答える