在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;
答案 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的'新'对象模型中是允许的。)