Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.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_Generics_Delphi Xe_Delphi Xe2 - Fatal编程技术网

Delphi 使用泛型类型数组的类成员调整奇怪的类实例大小

Delphi 使用泛型类型数组的类成员调整奇怪的类实例大小,delphi,generics,delphi-xe,delphi-xe2,Delphi,Generics,Delphi Xe,Delphi Xe2,下面是一段非常简单的代码,它模仿了我的一些代码中的类结构(表单只包含一个附加到单击事件的按钮)。我正在使用Delphi XE和XE II,在销毁此类所基于的生产代码中的对象时,会看到严重的崩溃。只有当我允许在Clear()方法中初始化TMyClass中的数组项的成员时,才会发生这些崩溃 不幸的是,到目前为止,在这个示例代码中,崩溃是不可重复的,但它确实模拟了一些非常奇怪的行为,我怀疑这可能是导致问题的原因 如果我在TMyClass.Clear函数中放置一个断点,并查看类reports 1288的

下面是一段非常简单的代码,它模仿了我的一些代码中的类结构(表单只包含一个附加到单击事件的按钮)。我正在使用Delphi XE和XE II,在销毁此类所基于的生产代码中的对象时,会看到严重的崩溃。只有当我允许在Clear()方法中初始化TMyClass中的数组项的成员时,才会发生这些崩溃

不幸的是,到目前为止,在这个示例代码中,崩溃是不可重复的,但它确实模拟了一些非常奇怪的行为,我怀疑这可能是导致问题的原因

如果我在TMyClass.Clear函数中放置一个断点,并查看类reports 1288的InstanceSize成员。这很奇怪,因为数组成员本身的大小实际上是12kb。我可以将提供的类型从TRecord2更改为Integer,并得到相同的InstanceSize结果。如果我完全从类中删除数组,我会得到一个大约264字节的实例大小,对于一个只包含一个128字节记录实例的类来说,这似乎是过高的。这表示编译器已将1024字节分配给TMyClass中V(TT类型)成员的数组存储

当我将12kb的数据写入一个编译器认为大小为1kb的对象时,我怀疑奇怪的实例大小会导致不好的事情发生

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

type
  TRecord = record A : Array[1..128] of byte; end;
  TRecord2 = packed record S : Single; T : TDateTime; end;

  TBase = class(TObject)
    public
      R : TRecord;
  end;

  TBase2<T> = class(TBase)
    public
      type
        TT = packed array [0..31, 0..31] of T;

      var
        V : TT;
        R2 : TRecord;
  end;

  TMyClass = class(TBase2<TRecord2>)
    public
      procedure Clear;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  O : TMyClass;
begin
  O := TMyClass.Create;
  O.Clear;
  O.Free;
end;

{ TMyClass }

procedure TMyClass.Clear;
var
  i, j : integer;
begin
  for i := 0 to 31 do
    for j := 0 to 31 do
      begin
        V[I, J].S := 0;
        V[I, J].T := 0;
      end;
end;

end.
单元1;
接口
使用
Winapi.Windows、Winapi.Messages、System.SysUtils、System.Variants、System.Classes、Vcl.Graphics、,
Vcl.控件、Vcl.窗体、Vcl.对话框、Vcl.stdctrl;
类型
TForm1=类(TForm)
按钮1:t按钮;
程序按钮1点击(发送方:ToObject);
私有的
{私有声明}
公众的
{公开声明}
结束;
变量
表1:TForm1;
实施
{$R*.dfm}
类型
TRecord=记录A:字节数组[1..128];结束;
TRecord2=打包记录S:单个;T:TDateTime;结束;
TBase=类别(TObject)
公众的
R:三叉戟;
结束;
TBase2=类别(TBase)
公众的
类型
TT=T的压缩数组[0..31,0..31];
变量
V:TT;
R2:三叉戟;
结束;
TMyClass=类别(TBase2)
公众的
程序清晰;
结束;
程序TForm1.按钮1单击(发送方:TObject);
变量
O:TMyClass;
开始
O:=TMyClass.Create;
O.清除;
O.免费;
结束;
{TMyClass}
程序TMyClass.Clear;
变量
i、 j:整数;
开始
对于i:=0到31 do
对于j:=0到31 do
开始
V[I,J].S:=0;
V[I,J].T:=0;
结束;
结束;
结束。

这是一个bug,如以下最小复制所示:

program InstanceSizeBug;

{$APPTYPE CONSOLE}

type
  TMyClass<T> = class
    V: array [0..255] of T;
  end;
  TMyClassSingle = class(TMyClass<Single>);

begin
  Writeln(TMyClass<Single>.InstanceSize);
  Writeln(TMyClassSingle.InstanceSize);
  Readln;
end.
请注意,在Delphi2010中也可以观察到相同的行为,这是我手头上唯一的另一个Delphi版本

在调试器下进行跟踪,您会发现自己处于
\u GetMem
中,
Size
参数等于
264
,显然没有分配足够的内存

如果有任何疑问,这个版本会因为AV而失败

program InstanceSizeBug;

{$APPTYPE CONSOLE}

type
  TMyClass<T> = class
    V: array [1..256*256] of T;
  end;
  TMyClassSingle = class(TMyClass<Single>);

begin
  TMyClassSingle.Create.V[256*256] := 0;
end.
程序实例izebug;
{$APPTYPE控制台}
类型
TMyClass=类
V:T的数组[1..256*256];
结束;
tmyclassingle=类(TMyClass);
开始
tmyclasssSingle.Create.V[256*256]:=0;
结束。
我已将此文件提交给质量中心


作为一种解决方法,我建议您使用动态数组,在构造函数中使用SetLength进行分配。我更愿意想象,是由于存在一个固定大小的泛型数组,导致编译器行为不正常。

我有点不知道问题出在哪里,但这些信息可能会有所帮助。大多数内置类都有Clear方法,当对象被销毁时,Clear方法会被自动调用。我会报告它。对于QC报告,谁进入真的很重要吗?此外,您似乎拥有最简洁的代码来重现它。嗨,大卫,谢谢您的关注。一定要为它创建一个高质量的中心项目:-)你认为这个问题有解决办法吗?谢谢,雷蒙德。我对这句话很感兴趣:tmyclassingle.Create.V[256*256]:=0;我以前从未见过这种语法!创建一个对象并在一行中写入一个字段只是一种懒惰的方式。我需要使阵列足够大,以击败内存管理器的子分配策略。很自然,它会漏水,但由于这个物体的尺寸都不合适,法官大人,我不认罪!我擅长一行中的创建和字段访问等,这是以这种方式将单个值分配给数组的新方法!
program InstanceSizeBug;

{$APPTYPE CONSOLE}

type
  TMyClass<T> = class
    V: array [1..256*256] of T;
  end;
  TMyClassSingle = class(TMyClass<Single>);

begin
  TMyClassSingle.Create.V[256*256] := 0;
end.