2012-12-25 52 views
1

我已經看過許多問題和資源,它們處理對象中的「自己」變量,但是每個人都說了一些不同的東西。德爾福 - 保持對一個對象的自我引用

例如,在這樣一個問題:Delphi Self-Pointer usage

收視率最高的問題的答案似乎是錯誤的。指針(自身)不指向包含它的對象,並且不能用於從對象內部傳遞引用。

我試着這樣做:

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」已經停止指向它的包含對象,並且我想知道堆中是否有物體被洗牌,或者內存是否被損壞。

編輯:

有在使用「自我」變量對我來說沒有工作的一些情況,現在我不能複製它。所以這整個問題都是毫無意義的,就像我使用'PSelf'變量的技巧一樣。對於那個很抱歉。

而且肯指出,上面的鏈接有一個正確的答案:)

+4

只是爲了您的信息,接受的答案,你認爲是「錯誤」的作者是[丹尼 - 索普(http://stackoverflow.com/users/301152/dthorpe),德爾福編譯器的前首席架構師(他在編譯器本身設計了特性),而他在Borland工作。 :-)我想他可能對他在說什麼有一個想法,他對這個問題的回答絕不是錯的。 ;-) –

+1

我恢復了你的編輯,因爲它使問題無效。這也不是StackOverflow的工作原理。 :-)如果你有一個解決方案發布,發佈它作爲你自己的問題的答案。 [FAQ](http://stackoverflow.com/faq)有關於這樣做的信息。增加更多的內容來解釋你發現的問題,從原始問題的意義上分心,並且也可以完全無效你已經收到的答案的含義。 (而意見爲好,這是你的去除你沒掛到我以前的上述評論文章段落的:-)) –

+0

我會得到它的竅門最終..:/ – AudioGL

回答

14

我想你誤會什麼Self是,如何對象引用在德爾福工作。

包含類實例的變量已經是指向該對象實例的指針。爲了方便起見,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 documentation

類類型的的變量實際上是引用一個對象的指針。因此,多個變量可以指向同一個對象。像其他指針一樣,類型變量可以保存值爲零。但是,您不必顯式取消引用類型變量來訪問它指向的對象。例如,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.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` 
+0

謝謝您的回答,肯。我編輯了原始問題。 – AudioGL

+0

@AudioGL:是的,我明白了。 ;-) 沒問題;如果將來對其他人有用,我會離開這個。 –

+0

不是我的意思是無視你寫得很好的文章或任何東西。我很驚訝,我首先犯了這個錯誤。 – AudioGL

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

這並不工作,但在我心目中是完全一樣的。這是什麼讓我不信任'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; 

我沒有在第一時間做到了這一點,因爲我擔心的是,不使用指針,我可能實際上正在複製整個對象。

+1

'Self'是方法內部的局部變量。通過在你的例子中使用一個指向self的指針('@ Self')並稍後使用它,你正在使用一個不再有效的指針。 –