总结:
对于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/

10-09 21:37