总结:
对于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.
最佳答案
代码
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'nilles'临时
堆栈变量,它不会
感;
构造函数调用分配
值到临时堆栈
变量,并产生记忆
泄漏。
为什么错误的代码有时可以正常工作(甚至可能不会导致内存泄漏)的问题是另一个问题。
再编辑一次
如前所述,Delphi对象是一个引用。因此,您无需使用
var
来更改对象。但是您的过程有所不同-它会更改引用本身,不仅更改这些引用所指向的数据,因此您应按引用传递这些引用(aRotationMatUser, aTranslationMatUser, aScalingMatUser
)。这就是为什么您需要var
的原因。关于delphi - 在快速绘画周期中使用释放的对象的异常(exception),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5184036/