我已经查看了许多问题和资源,这些问题和资源都涉及对象中"自我"变量,但是每个人都说了不同的话。
例如,在这个问题中:delphi自我点使用
这个问题的最高评分答案似乎是错误的。指针(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
.
.
.
,在大多数情况下,这一切都很好。
我的问题是:这是一个很好的编码实践吗?可以期望" p self"变量在程序执行期间指向对象?
我最近遇到了一个错误," pself"已经停止指向其包含对象,我想知道对象是否曾经在堆中散布,或者记忆是否已损坏。
编辑:
在某些情况下,使用"自我"变量对我不起作用,现在我无法复制它。因此,整个问题是毫无意义的,我使用" pself"变量的技术也是如此。抱歉。
正如肯指出的那样,上面的链接有一个正确的答案:)
我认为您误解了Self
是什么,以及对象引用在Delphi中的工作方式。
包含类实例的变量已经是指向该对象实例的指针。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将值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.GetMyInteger
和TMyObject.SetMyInteger
中可用且有效(在我的示例中唯一实现的代码),并且始终涉及当前实例。
无需跟踪Self
的地址,因为变量引用该对象实例的方法中的对象实例 IS 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`
这是我对问题的解决方案。我仍然想知道为什么第二个示例不起作用。
这有效(但这是错误的方法):
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;
我首先没有这样做,因为我担心不使用指针,我实际上可能正在重复整个对象。