Delphi 使用泛型类型数组的类成员调整奇怪的类实例大小
下面是一段非常简单的代码,它模仿了我的一些代码中的类结构(表单只包含一个附加到单击事件的按钮)。我正在使用Delphi XE和XE II,在销毁此类所基于的生产代码中的对象时,会看到严重的崩溃。只有当我允许在Clear()方法中初始化TMyClass中的数组项的成员时,才会发生这些崩溃 不幸的是,到目前为止,在这个示例代码中,崩溃是不可重复的,但它确实模拟了一些非常奇怪的行为,我怀疑这可能是导致问题的原因 如果我在TMyClass.Clear函数中放置一个断点,并查看类reports 1288的InstanceSize成员。这很奇怪,因为数组成员本身的大小实际上是12kb。我可以将提供的类型从TRecord2更改为Integer,并得到相同的InstanceSize结果。如果我完全从类中删除数组,我会得到一个大约264字节的实例大小,对于一个只包含一个128字节记录实例的类来说,这似乎是过高的。这表示编译器已将1024字节分配给TMyClass中V(TT类型)成员的数组存储 当我将12kb的数据写入一个编译器认为大小为1kb的对象时,我怀疑奇怪的实例大小会导致不好的事情发生Delphi 使用泛型类型数组的类成员调整奇怪的类实例大小,delphi,generics,delphi-xe,delphi-xe2,Delphi,Generics,Delphi Xe,Delphi Xe2,下面是一段非常简单的代码,它模仿了我的一些代码中的类结构(表单只包含一个附加到单击事件的按钮)。我正在使用Delphi XE和XE II,在销毁此类所基于的生产代码中的对象时,会看到严重的崩溃。只有当我允许在Clear()方法中初始化TMyClass中的数组项的成员时,才会发生这些崩溃 不幸的是,到目前为止,在这个示例代码中,崩溃是不可重复的,但它确实模拟了一些非常奇怪的行为,我怀疑这可能是导致问题的原因 如果我在TMyClass.Clear函数中放置一个断点,并查看类reports 1288的
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.