2011-03-03 41 views
1

綜述的:
對於Delphi函數/程序,如果一個類的一個實例是通過傳遞作爲參數,另一個參考(除了原有的參考)被臨時創建調用堆棧指向該實例並在本地使用。因此,注意:異常快速畫循環使用釋放的對象

(1)如果函數/過程只想更改該實例的內容/字段/屬性,則不需要var前綴; (2)如果函數/過程可能想要將引用重新分配給新實例,請使用var前綴,或者它是重新分配的臨時引用。 (3)注意,如果函數/過程重新分配引用並且不使用var前綴,則結果可能是正確的,這更糟糕,因爲最終代碼將在某一天中斷。

=======================================
的情況是:
這是一個小應用程序。 TMolForm是一個MDIChild表單,每個TMolForm都包含一個TMolScene,它是從TPaintBox開始的。 TMolScene吸引了TMol。在TMolScene的繪製過程中,如果調整TMolScene的大小,TMolScene將調用TMol.Rescale。然後TMolScene調用TMol.TransformCoordinates爲後續渲染建立座標。

的問題是:
現在,在TMol.Rescale,我重置通過來電,TMolScene通過矩陣。但是,我遇到了一些我無法想到的原因。

(1)具體地,如果餘有多個 TMolForm,和迅速做調整大小,鼠標拖動(其是分子旋轉),TMolForm之間的切換,在不到5分鐘,突然的矩陣(假定已經重置了在TMol.Rescale中)傳遞給TMol.TransformCoordinates爲零或包含零內容。 (2)如果我啓用FastMM4及其FullDebugMode,並重覆上述鼠標移動,我可以得到「TMol.Rescale嘗試釋放釋放的對象」。看起來TMol.Rescale在最後一次調用(或最後一次繪製週期)未完成時再次調用。我的意思是,我沒有做任何涉及多線程的嘗試,當最後一次調用還沒有返回時,TMol.Rescale怎麼可能第二次調用呢? 我完全失去了。你能否幫助評論任何可能的原因? (3)如果我從TMol.Rescale中刪除了矩陣的重置,並且調用了它的調用者TMolScene.OnScenePaint,那麼異常似乎不會發生,至少在5分鐘內不會發生。 (我沒有迅速濫用鼠標超過5分鐘。也許有其他更好的測試方式。)我不知道爲什麼這個工程,爲什麼上述崩潰有時。 (4)如果我只有一個TMolform,上面的例外似乎不會發生,至少在5分鐘內不會發生。

我必須承認,我編造了以下最小化的代碼以捕捉異常。但是,雖然執行程序應該反映真實情況,但例外不會發生。如果您希望看到我願意通過電子郵件或其他方式發送給您的真實代碼。不過,這是愛好和不是寫得很好,但是,對不起。

任何有關例外情況或錯誤編碼習慣的建議都非常感謝。

 unit uMolForm; 

     interface 

     uses 
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
      ExtCtrls, Dialogs; 

     type 
      TVec = class; 
      TMat = class; 
      TMol = class; 
      TMolScene = class; 
      TMolForm = class; 

      TVec = class 
      public 
      X, Y, Z: Extended; 
      constructor Create; overload; 
      constructor Create(aX, aY, aZ: Extended); overload; 
      end; 

      TMat = class 
      private 
      FX, FY, FZ, FT: TVec; 
      public 
      property X: TVec read FX; 
      property Y: TVec read FY; 
      property Z: TVec read FZ; 
      constructor Create; 
      destructor Destroy; override; 
      function ToUnit: TMat; 
      end; 

      TMol = class 
      public 
      constructor Create; 
      destructor Destroy; override; 
      procedure Rescale(aBbWidth, aBbHeight: Integer; 
       aRotationMatUser, aTranslationMatUser, aScalingMatUser: TMat); 
      procedure TransformCoordinates(aBbWidth, aBbHeight: Integer; 
       aRotationMatUser, aTranslationMatUser, aScalingMatUser: TMat); 
      end; 

      TMolScene = class(TPaintBox) 
      private 
      FBbWidth, FBbHeight: Integer; 
      FRotationMat, FTranslationMat, FScalingMat: TMat; 
      FMol: TMol; 
      procedure OnScenePaint(Sender: TObject); 
      procedure OnSceneMouseDown(Sender: TObject; Button: TMouseButton; 
       Shift: TShiftState; X, Y: Integer); 
      procedure OnSceneMouseUp(Sender: TObject; Button: TMouseButton; 
       Shift: TShiftState; X, Y: Integer); 
      procedure OnSceneMouseMove(Sender: TObject; Shift: TShiftState; 
       X, Y: Integer); 
      public 
      constructor Create(AOwner: TComponent); 
      destructor Destroy; override; 
      end; 

      TMolForm = class(TForm) 
      procedure FormClose(Sender: TObject; var Action: TCloseAction); 
      procedure FormCreate(Sender: TObject); 
      private 
      { Private declarations } 
      FMolScene: TMolScene; 
      public 
      { Public declarations } 
      end; 

     implementation 

     {$R *.dfm} 

     { TVec } 

     constructor TVec.Create; 
     begin 
      inherited; 

      X := 0; 
      Y := 0; 
      Z := 0; 
     end; 

     constructor TVec.Create(aX, aY, aZ: Extended); 
     begin 
      inherited Create; 

      X := aX; 
      Y := aY; 
      Z := aZ; 
     end; 

     { TMat } 

     constructor TMat.Create; 
     begin 
      inherited; 

      ToUnit; 
     end; 

     destructor TMat.Destroy; 
     begin 
      FreeAndNil(FX); 
      FreeAndNil(FY); 
      FreeAndNil(FZ); 
      FreeAndNil(FT); 

      inherited; 
     end; 

     function TMat.ToUnit: TMat; 
     begin 
      FreeAndNil(FX); 
      FreeAndNil(FY); 
      FreeAndNil(FZ); 
      FreeAndNil(FT); 

      FX := TVec.Create(1, 0, 0); 
      FY := TVec.Create(0, 1, 0); 
      FZ := TVec.Create(0, 0, 1); 
      FT := TVec.Create; 

      Result := Self; 
     end; 

     { TMol } 

     constructor TMol.Create; 
     begin 
      inherited; 

     end; 

     destructor TMol.Destroy; 
     begin 

      inherited; 
     end; 

     procedure TMol.Rescale(aBbWidth, aBbHeight: Integer; 
      aRotationMatUser, aTranslationMatUser, aScalingMatUser: TMat); 
     begin 

      FreeAndNil(aRotationMatUser); 
      FreeAndNil(aTranslationMatUser); 
      FreeAndNil(aScalingMatUser); 

      aRotationMatUser := TMat.Create; 
      aTranslationMatUser := TMat.Create; 
      aScalingMatUser := TMat.Create; 
     end; 

     procedure TMol.TransformCoordinates(aBbWidth, aBbHeight: Integer; 
      aRotationMatUser, aTranslationMatUser, aScalingMatUser: TMat); 
     begin 

      if (aRotationMatUser.X = nil) or (aRotationMatUser.Y = nil) or 
      (aRotationMatUser.Z = nil) or (aTranslationMatUser.X = nil) or 
      (aTranslationMatUser.Y = nil) or (aTranslationMatUser.Z = nil) or 
      (aScalingMatUser.X = nil) or (aScalingMatUser.Y = nil) or 
      (aScalingMatUser.Z = nil) then 
      begin 
      raise Exception.Create('what happened?!'); 
      end; 
     end; 

     { TMolScene } 

     constructor TMolScene.Create(AOwner: TComponent); 
     begin 
      inherited; 

      FRotationMat := TMat.Create; 
      FTranslationMat := TMat.Create; 
      FScalingMat := TMat.Create; 
      FMol := TMol.Create; 

      Self.OnPaint := Self.OnScenePaint; 
      Self.OnMouseDown := Self.OnSceneMouseDown; 
      Self.OnMouseUp := Self.OnSceneMouseUp; 
      Self.OnMouseMove := Self.OnSceneMouseMove; 
     end; 

     destructor TMolScene.Destroy; 
     begin 
      FreeAndNil(FRotationMat); 
      FreeAndNil(FTranslationMat); 
      FreeAndNil(FScalingMat); 
      FreeAndNil(FMol); 

      inherited; 
     end; 

     procedure TMolScene.OnScenePaint(Sender: TObject); 
     begin 
      if (FBbWidth <> Self.ClientWidth) or (FBbHeight <> Self.ClientHeight) then 
      begin 
      FBbWidth := Self.ClientWidth; 
      FBbHeight := Self.ClientHeight; 
      FMol.Rescale(FBbWidth, FBbHeight, FRotationMat, FTranslationMat, 
       FScalingMat); 
      end; 

      FMol.TransformCoordinates(FBbWidth, FBbHeight, FRotationMat, FTranslationMat, 
      FScalingMat); 
     end; 

     procedure TMolScene.OnSceneMouseDown(Sender: TObject; Button: TMouseButton; 
      Shift: TShiftState; X, Y: Integer); 
     begin 
      Self.Repaint; 
     end; 

     procedure TMolScene.OnSceneMouseUp(Sender: TObject; Button: TMouseButton; 
      Shift: TShiftState; X, Y: Integer); 
     begin 
      Self.Repaint; 
     end; 

     procedure TMolScene.OnSceneMouseMove(Sender: TObject; Shift: TShiftState; X, 
      Y: Integer); 
     begin 
      Self.Repaint; 
     end; 

     { TMolForm } 

     procedure TMolForm.FormCreate(Sender: TObject); 
     begin 
      FMolScene := TMolScene.Create(Self); 
      FMolScene.Parent := Self; 
      FMolScene.Align := alClient; 
     end; 

     procedure TMolForm.FormClose(Sender: TObject; var Action: TCloseAction); 
     begin 
      Action := caFree; 
     end; 

     end. 
+1

如果您對性能感到困擾,最好使用雙精度比擴展 – 2011-03-03 17:41:02

+0

@David:感謝您的評論!我的確是!然後,我應該從最佳實踐中學習。 – SOUser 2011-03-03 18:10:26

+1

更重要的是,對於真實的表演,你不需要TVec作爲一個班級。記錄好得多。你得到堆棧分配並有機會使用操作符重載。 – 2011-03-03 19:54:48

回答

4

代碼

procedure TMol.Rescale(aBbWidth, aBbHeight: Integer; 
     aRotationMatUser, aTranslationMatUser, aScalingMatUser: TMat); 
    begin 

     FreeAndNil(aRotationMatUser); 
     FreeAndNil(aTranslationMatUser); 
     FreeAndNil(aScalingMatUser); 

     aRotationMatUser := TMat.Create; 
     aTranslationMatUser := TMat.Create; 
     aScalingMatUser := TMat.Create; 
    end; 

是錯誤的。你應該按引用傳遞aRotationMatUser, aTranslationMatUser, aScalingMatUser參數:

procedure TMol.Rescale(aBbWidth, aBbHeight: Integer; 
     **var** aRotationMatUser, aTranslationMatUser, aScalingMatUser: TMat); 

您應該使用VAR來傳遞參數在上述過程中,因爲沒有它

  • FreeAndNil「尼勒斯」臨時 堆棧變量,它沒有 的意義;
  • 構造函數調用將 值分配給臨時堆棧 變量,導致內存 泄漏。

問題爲什麼錯誤代碼有時可以正常工作(甚至可能不會導致內存泄漏)是一個不同的故事。


還有一個編輯

正如你已經提到德爾福對象的引用。所以你不需要使用var來改變對象。但是你的過程是不同的 - 它改變了引用本身,不僅僅是這些引用指向的數據,所以你應該通過引用傳遞這些引用(aRotationMatUser, aTranslationMatUser, aScalingMatUser)。這就是爲什麼你需要var

+0

@Serg:謝謝你的幫助!然後,我不明白爲什麼它只在打開一個TMolForm時起作用?例外發生得非常隨機。此外,由於TMat是類,不是「通過引用」已經是通過參數傳遞的方式嗎? – SOUser 2011-03-03 17:28:20

+1

德爾福班是一個參考,但你需要'參考參考'在上述程序 – kludg 2011-03-03 17:32:41

+0

@Serg:感謝您的評論!你能否提出我可以學到的有關該術語的任何材料?此外,我仍然不知道爲什麼它只是隨機發生。儘管如此,我只是添加了var關鍵字,並會再嘗試幾分鐘並讓您知道。 – SOUser 2011-03-03 17:40:24