1

我查看了许多处理对象中“Self”变量的问题和资源,但每个人都说不同的话。

例如,在这个问题中:Delphi Self-Pointer usage

该问题的最高评分答案似乎是错误的。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;

德尔福文档

类类型的变量实际上是一个引用对象的指针。因此,不止一个变量可以引用同一个对象。像其他指针一样,类类型变量可以保存值 nil。但是您不必显式取消引用类类型变量来访问它指向的对象。例如, SomeObject.Size := 100 将值 100 分配给 SomeObject 引用的对象的 Size 属性;你不会把它写成 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.GetMyInteger它在及以上(我的示例中唯一实现的代码)中可用且有效TMyObject.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

这是我对问题的解决方案。我仍然想知道为什么第二个示例不起作用。

这有效(但这样做的方法是错误的):

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
  .
  .
  .

这不起作用,但在我的脑海中是完全一样的。这就是导致我不信任“自我”变量的原因(尽管是的,我正在创建一个指向指针的指针)。

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 回答