调用继承错误造成的损害是什么?

时间:2011-12-31 19:46:58

标签: delphi

在Delphi XE程序中搜索高度间歇性的内存损坏时,我发现了一个类构造函数,它初始化了类中的几个字段,然后然后调用继承。我相信初始化是在构造函数首次编写后添加的,并且意外地在错误的位置。我现在已经纠正它,先调用继承。内存损坏的例外情况几乎总是发生在这个类的方法中。

问题:这个错误是否可能导致间歇性内存损坏?在跟踪代码时,似乎没有,但我真的希望这个解决方案可以解决间歇性问题。在解决问题后不会发生一段时间并不会证明它已经消失了。

一些代码:

Tmyclass = class
  ctype : integer;
  ts : tstringlist;
  th : thandle;
public
  Constructor Create;
  Destructor Destroy; override;
  ...
end;

Constructor Tmyclass.Create;
begin
  ctype := 3;
  doinit;
  inherited;
end;

3 个答案:

答案 0 :(得分:5)

以下是对象创建的典型步骤:

  • 对象实例的内存分配;
  • 用零填充所有内存(初始化所有字段,尤其是字符串);
  • 从最新的子节点开始,调用所有嵌套的构造函数,让inherited调用每个父节点 - 这就是为什么你应该在构造函数和析构函数中编写inherited

因此,inherited调用父方法 - 您甚至可以指定父级别,或者如果您确定可以执行此操作则调用none(但可能会违反SOLID原则)。

实际上,当调用constructor时,会在方法中添加隐藏参数:

  

构造函数和析构函数使用与其他调用约定相同的调用约定   方法,除了传递一个额外的布尔标志参数   表示构造函数或析构函数调用的上下文。

     

构造函数调用的flag参数中的值为False   通过实例对象或使用调用构造函数   继承的关键字。在这种情况下,构造函数的行为类似于   普通方法。 a的flag参数中的值为True   构造函数调用表明构造函数是通过一个调用的   课堂参考。在这种情况下,构造函数创建一个实例   Self给出的类,并返回对新创建的引用   EAX中的对象。

     

析构函数调用的flag参数中的值为False   使用inherited关键字调用析构函数。在这   例如,析构函数的行为类似于普通方法。值为True   在析构函数调用的flag参数中指示   析构函数是通过实例对象调用的。在这种情况下,   析构函数释放出Self之前给出的实例   返回。

     

flag参数的行为就像它在所有其他参数之前声明一样   参数。根据注册约定,它在DL中传递   寄存器。在pascal惯例下,它被推到了所有其他之前   参数。在cdecl,stdcall和safecall约定下,它是   在Self参数之前推送。

来源:official Delphi documentation

因此,您可以确定,无论何处调用inherited,都可以安全地处理它。例如,在调用所有构造函数之前,字段的初始化(重置为0)将只处理一次。

TObject.Create默认构造函数(在inherited行中调用的构造函数)只是一个begin end void块,它什么都不做。在这里调用inherited甚至没有必要/强制要求,但这是一个好习惯,因为如果你改变你的对象层次结构,它可能是需要的。

唯一的问题可能是,如果在这个inherited方法(ctype := 2之类)中设置了一些字段,在孩子中设置后 - 但这不是编译器的错误,这取决于用户代码!

答案 1 :(得分:1)

在调用继承的构造函数之前初始化一些字段不一定是错误。有时,继承的构造函数会调用一些已被子代覆盖的虚方法,这些新实现依赖于这些字段的正确初始化。

(我不是说这是好设计,但它不是一个错误。)

答案 2 :(得分:0)

在Delphi中,您可以在调用继承的构造函数之前初始化对象字段(它在Turbo Pascal或“旧”对象模型中不起作用,但在Delphi的'新'对象模型中是允许的。)