易错的Delphi程序在Linux中出现段错误 - Windows没有问题。

3

我有一个简单的程序,使用Delphi 10.2编写,在Windows上运行良好,但在Linux上崩溃。

关键是所使用的类在其析构函数中有待执行的代码。

type
  Kwak = class
  public
    index: integer;
    constructor Create(index:integer);
    destructor Free;
  end;

constructor Kwak.Create(index:integer);
begin
  self.index := index;
  writeln('Welcome Kwak '+inttostr(index));
end;

destructor Kwak.Free;
begin
  writeln('Bye Kwak '+inttostr(index));
end;

如果我在调用程序中使用它,就像这样:
procedure myProc1;
var 
  myKwak:Kwak;
begin
  myKwak := Kwak.Create(15);
  myKwak.Free;
end;

这段代码在Windows上运行良好,但在Linux上当myKwak离开作用域时(在myProc1中遇到end)会导致分段错误。

我猜想这一切都与Linux编译器上的自动引用计数有关。

如果我使用FreeAndNil(),程序就不会崩溃,但也不会调用析构函数。

有没有优雅的解决方案呢?

  • 我的程序中有很多像这样的Free。当然,将Free代码转移到其他地方是可行的,但我更喜欢更优雅的方法。
  • 程序需要在Windows上回到XE2,在Linux上回到10.2进行编译。我读到10.3不包括ARC,这可能解决问题,但是10.3很昂贵。
  • 最好尽量减少程序更改和{$IFDEF ...}指令。

请告诉我您的建议。


2
Destroy()定义为析构函数并覆盖它,而不是使用Free()。调用Free()来销毁实例仍然是正确的。请参见此示例 - AmigoJack
1个回答

7
destructor Free; // <-- WRONG!

这是错误的。正确的析构函数被称为 Destroy(),并且在 TObject 中是 virtual 的,因此您需要在派生类中进行 override

type 
  Kwak = class
  public
    index: integer;
    constructor Create(index:integer);
    destructor Destroy; override;
  end;

constructor Kwak.Create(index:integer);
begin
  inherited Create;
  self.index := index;
  writeln('Welcome Kwak '+inttostr(index));
end;

destructor Kwak.Destroy;
begin
  writeln('Bye Kwak '+inttostr(index));
  inherited;
end;

在非ARC系统中,TObject.Free()是一个非虚拟的实例方法,如果Self不为nil,则调用Destroy()析构函数。
在ARC系统中,编译器会自动将所有对Free()的调用替换为nil赋值,并减少对象的引用计数。这样可以在ARC和非ARC系统上使用相似的语义来使用相同的代码。当对象的引用计数降至0时,会调用它的Destroy()析构函数。

值得注意的是,从10.3 Rio开始,Linux编译器已经放弃了ARC并回归传统内存管理(以反映Windows编译器)。此时,升级并忘记ARC几乎是值得的,因为它终将消失。虽然这不会影响此代码,但如果OP的其余代码依赖于ARC进行内存管理,则升级到Tokyo之外将需要进行重大重构。 - J...
1
@J... ARC已经消失了,因为10.4放弃了所有平台的ARC - Remy Lebeau
确实,我提到了东京/里约,因为OP正在使用Linux,但是ARC是完全没有前途的。也许“消失”这个词用得不好 - 我的意思是从OP的角度来看,就他们的升级前景而言。现在投资于依赖ARC的新开发只会挖掘技术债务的深坑。 - J...
感谢Remy。将析构函数free替换为destroy(进行覆盖)使其像魔术般运行。非常感谢您的帮助。 - JP Hendriks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接