Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi 在快速绘制循环中使用释放对象的例外情况_Delphi_Exception_Free_Paint - Fatal编程技术网

Delphi 在快速绘制循环中使用释放对象的例外情况

Delphi 在快速绘制循环中使用释放对象的例外情况,delphi,exception,free,paint,Delphi,Exception,Free,Paint,总结: 对于Delphi函数/过程,如果类的实例作为参数传递,则在临时调用堆栈上创建除原始引用之外的另一个引用,以指向该实例,并在本地使用。因此,请注意: 1如果函数/过程只想更改该实例的内容/字段/属性,则不需要var前缀 2如果函数/过程可能希望将引用重新分配给新实例,请使用var前缀,或者重新分配的是临时引用 3注意,如果函数/过程重新分配了引用,并且没有使用var前缀,那么结果可能是正确的,更糟糕的是,因为代码终有一天会中断 ===============================

总结: 对于Delphi函数/过程,如果类的实例作为参数传递,则在临时调用堆栈上创建除原始引用之外的另一个引用,以指向该实例,并在本地使用。因此,请注意:

1如果函数/过程只想更改该实例的内容/字段/属性,则不需要var前缀

2如果函数/过程可能希望将引用重新分配给新实例,请使用var前缀,或者重新分配的是临时引用

3注意,如果函数/过程重新分配了引用,并且没有使用var前缀,那么结果可能是正确的,更糟糕的是,因为代码终有一天会中断

======================================= 情况是: 这是一个小应用程序。TMolForm是一个MDIChild表单,每个TMolForm都包含一个从TPaintBox派生的TMolScene。TMol场景绘制TMol。在TMolScene的绘制过程中,如果调整了TMolScene的大小,TMolScene将调用TMol.Rescale。然后,TMolScene调用TMol.TransformCoordinates为后续渲染建立坐标

问题是: 现在,在TMol.Rescale中,我重置了调用程序TMolScene传递的矩阵。然而,我遇到了一些例外,我想不出原因

1特别是,如果我有多个TMolForm,并快速调整大小,鼠标拖动(分子旋转),在TMolForm之间切换,在不到5分钟的时间内,矩阵应该已经在TMol中重置。传递到TMol的重缩放。Transform坐标为零或包含零内容

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;
这是一个错误。您应该通过引用传递ArotationStatus、AtransationStatus、aScalingMatUser参数:

    procedure TMol.Rescale(aBbWidth, aBbHeight: Integer;
      **var** aRotationMatUser, aTranslationMatUser, aScalingMatUser: TMat);
您应该使用var来传递上述过程中的参数,因为没有var

FreeAndNil‘nilles’临时 堆栈变量,它不会 感觉 构造函数调用assign 临时堆栈中的值 变量,并产生内存 泄漏。 为什么错误代码有时工作正常,甚至可能不会导致内存泄漏,这是另一个问题

再编辑一次

正如您已经提到的,Delphi对象是一个引用。因此,您不需要使用var来更改对象。但是您的过程是不同的-它会更改引用本身,而不仅仅是这些引用所指向的数据,因此您应该逐个引用地传递这些引用ArotationStatuser、AtransationStatuser、aScalingMatUser。这就是为什么需要变量。

代码

    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;
这是一个错误。您应该通过引用传递ArotationStatus、AtransationStatus、aScalingMatUser参数:

    procedure TMol.Rescale(aBbWidth, aBbHeight: Integer;
      **var** aRotationMatUser, aTranslationMatUser, aScalingMatUser: TMat);
您应该使用var来传递上述过程中的参数,因为没有var

FreeAndNil‘nilles’临时 堆栈变量,它不会 感觉 构造函数调用assign 临时堆栈中的值 变量,并产生内存 泄漏。 为什么错误代码有时工作正常,甚至可能不会导致内存泄漏,这是另一个问题

再编辑一次


正如您已经提到的,Delphi对象是一个引用。因此,您不需要使用var来更改对象。但是您的过程是不同的-它会更改引用本身,而不仅仅是这些引用所指向的数据,因此您应该逐个引用地传递这些引用ArotationStatuser、AtransationStatuser、aScalingMatUser。这就是您需要var的原因。

@Serg:谢谢您的帮助!那我就不明白为什么当我

打开一个TMolForm?例外情况是随机发生的。此外,由于TMat是类,引用不是已经是传递参数的方式了吗?Delphi类是一个引用,但在上面的例子中需要“引用到引用”procedure@Serg:谢谢你的评论!你能推荐一些我能学到的关于这个术语的材料吗?此外,我仍然不知道为什么它只是随机发生的。尽管如此,我只是添加了var关键字,并将再尝试几分钟,让您知道。@Serg:将var添加到参数列表中可以消除异常!非常感谢你!我不知何故仍然迷茫1为什么在真实代码中异常只是随机产生的2上面的演示代码不产生异常?另外,您是否可以帮助评论在参数中使用var前缀的最佳实践?我的意思是,我似乎记得一个代码示例,其中一个tstringlist被传递以进行修改,但没有var前缀。@Serg:顺便说一下,我找到了这个链接。Nick的答案通过了一个要修改的TStrings实例,但没有var前缀。你能解释一下为什么在这种情况下它是安全的吗?另外,您能否帮助评论一下使用var前缀的最佳做法?@Serg:谢谢您的帮助!那我就不明白为什么我只打开一个TMolForm就可以了?例外情况是随机发生的。此外,由于TMat是类,引用不是已经是传递参数的方式了吗?Delphi类是一个引用,但在上面的例子中需要“引用到引用”procedure@Serg:谢谢你的评论!你能推荐一些我能学到的关于这个术语的材料吗?此外,我仍然不知道为什么它只是随机发生的。尽管如此,我只是添加了var关键字,并将再尝试几分钟,让您知道。@Serg:将var添加到参数列表中可以消除异常!非常感谢你!我不知何故仍然迷茫1为什么在真实代码中异常只是随机产生的2上面的演示代码不产生异常?另外,您是否可以帮助评论在参数中使用var前缀的最佳实践?我的意思是,我似乎记得一个代码示例,其中一个tstringlist被传递以进行修改,但没有var前缀。@Serg:顺便说一下,我找到了这个链接。Nick的答案通过了一个要修改的TStrings实例,但没有var前缀。你能解释一下为什么在这种情况下它是安全的吗?另外,您能否帮助评论一下使用var前缀的最佳实践是什么?如果您担心性能问题,最好使用双精度,而不是extended@David:谢谢你的评论!真的!然后我应该从最佳实践中学习。更重要的是,为了真正的表现,你不希望TVec成为一个班级。最好是一张唱片。您将获得堆栈分配和使用运算符重载的机会。@David Heffernan:非常感谢您的建议!我同意使用记录和堆栈分配可以提高性能。然而,在当前的代码中,TVec有一些程序,如加法/减法/比例/长度等。在Delphi7中,Lazarus试图保持兼容的版本,似乎我无法为记录类型定义函数/过程。操作符重载也是一个非常新的特性。保持代码与Lazarus兼容可以为Mac编译。我使用您的完整用户名,因为StackOverflow说它可以将消息发送给消息接收者。@Xichen好的,我没有意识到您正在尝试跨平台发送消息。你可以考虑使用对象-不知道Lazarus是否支持。这是一个古老的Turbo-Pascal结构。它本质上相当于一个带有方法的记录。对于3向量的堆栈分配,不仅仅是代码运行得更快,而且对于处理此类类型的代码,通常最好使用值类型语义而不是引用语义。那是我个人的看法!如果您担心性能问题,最好使用双精度,而不是extended@David:谢谢你的评论!真的!然后我应该从最佳实践中学习。更重要的是,为了真正的表现,你不希望TVec成为一个班级。最好是一张唱片。您将获得堆栈分配和使用运算符重载的机会。@David Heffernan:非常感谢您的建议!我同意使用记录和堆栈分配可以提高性能。然而,在当前的代码中,TVec有一些程序,如加法/减法/比例/长度等。在Delphi7中,Lazarus试图保持兼容的版本,似乎我无法为记录类型定义函数/过程。操作符重载也是一个非常新的特性。保持代码与Lazarus兼容可以为Mac编译。我使用您的完整用户名,因为StackOverflow说它可以将消息发送给消息接收者。@Xichen好的,我没有意识到您正在尝试跨平台发送消息。你可以考虑使用对象-不知道Lazarus是否支持。这是一个旧的涡轮帕斯卡控制器 uct。它本质上相当于一个带有方法的记录。对于3向量的堆栈分配,不仅仅是代码运行得更快,而且对于处理此类类型的代码,通常最好使用值类型语义而不是引用语义。那是我个人的看法!