綜述的:
對於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.
如果您對性能感到困擾,最好使用雙精度比擴展 – 2011-03-03 17:41:02
@David:感謝您的評論!我的確是!然後,我應該從最佳實踐中學習。 – SOUser 2011-03-03 18:10:26
更重要的是,對於真實的表演,你不需要TVec作爲一個班級。記錄好得多。你得到堆棧分配並有機會使用操作符重載。 – 2011-03-03 19:54:48