TObject.Create 与 TComponent.Create 之间的关系 - 关于 A Object 的创建过程 (100分)

  • 主题发起人 主题发起人 savetime
  • 开始时间 开始时间
S

savetime

Unregistered / Unconfirmed
GUEST, unregistred user!
最近在读 VCL 源代码,被 TObject 和 TComponent 的 Create 方法弄糊涂了:

TObject.Create 调用 System._ClassCreate, System._ClassCreate 又调用
TObject.NewInstance, TObject.NewInstace 调用 TObject.InitInstance,
最后又调用了 TObject.AfterConstruction.

TObject.Create 不是虚方法.

可是到了 TComponent, TComponent.Create(AOwner: TComponent); virtual 覆盖了
TObject.Create, 那么考查以下语句:

var
AObj: TComponent;
begin
AObj := TComponet.Create(nil);
end;

这时 TComponent.Create 如何能够调用 TObject.Create 方法呢?是不是
Object.Create 方法一定会在任何类的 Constructor 函数之前自动调用。
如果是这样,那么 TObject.AfterConstructor 应该是在 Constructor 方
法之前调用,而这时(假设为TComponent) TComponent.Create(AOwner) 还
没有执行,也就是说继承类还没有初始化之前就调用AfterConstructor,
这显然是不对的。

希望有人能解释一下 TComponent.Create(AOwner: TComponent) 的编译器实现过程。

另外还有一个相关问题,TThreadList的定义如下:
TThreadList = class { 从TObject继承而来 }
constructor TThreadList.Create;
begin
inherited Create; { 这里的inherited Create是指什么,莫非是TObject.Create?}
InitializeCriticalSection(FLock); { 使用Windows函数调用建立临界区 }
FList := TList.Create;
FDuplicates := dupIgnore;
end;

TTheadList是从 TObject 继承下来的,它的 Create 方法中有一句 inherited Create;
这个inherited Create是调用 TObject.Create 吗?而 TObject.Create 不是虚方法。

希望有人能解释这个语句的意义。
 
我几乎阅读了整个 Delphi 的 Object Pascal Reference 及 TObject, TPersisent,
TComponent 的源码,可是还是不理解对象的创建过程,请高手指点。
对此问题感兴趣的人请帮忙把问题提前。
 
AnObject := TObject.Create;

编译器将其编译实现为:

用TObject对应的VMT为依据,调用TObject的Create构造函数。而在Create构造函数调用了系统的ClassCreate过程,系统的ClassCreate过程又通过存储在类VMT调用NewInstance虚方法。调用NewInstance方法的目的是要建立对象的实例空间,因为我们没有重载该方法,所以,它就是TObject类的NewInstance。TObjec类的NewInstance方法将根据编译器在VMT表中初始化的对象实例尺寸(InstanceSize),调用GetMem过程为该对象分配内存,然后调用InitInstance方法将分配的空间初始化。InitInstance方法首先将对象空间的头4个字节初始化为指向对象类对应VMT的指针,然后将其余的空间清零。建立对象实例之后,还调用了一个虚方法AfterConstruction。最后,将对象实例数据的地址指针保存到AnObject变量中,这样,AnObject对象就诞生了。
 
也就是说继承类还没有初始化之前就调用AfterConstructor,
这显然是不对的

您可以看看AfterConstructor在TObject中的声明,是Virtual,在TComponent并没有进行任何的重载,下面的一段话可能会让您清楚一些。
而将AfterConstruction和BeforeDestruction声明为虚函数,也是为了将来派生的类在产生对象之后,有机会让新诞生的对象呼吸第一口新鲜空气,而在对象消亡之前可以允许对象完成善后事宜,这都是合情合理的事。其实,TForm对象和TDataModule对象的OnCreate事件和OnDestroy事件,就是在TForm和TDataModule重载的这两个虚函数过程分别触发的。
 
您可以在网上找这个:第二章 DELPHI的原子世界
祝您好运[:)]
 
savetime:
你真的是个有心人,很少人去研究这些;
但我告诉你 Tobject.Create 确实是个 virtual 方法,不仅是 virtual方法,而且是个Class 方法。
其实它应该这样声明:
class constructor procedure Create; virtural;
但Borland没有这样做,这是Borand内部编译器支持的,你无法理解。
 
to Camel_bbs:
李战的这篇文章我读了好几遍了,你刚才写的那段和我问题中的一样。

TObject.Create 调用 System._ClassCreate, System._ClassCreate 又调用
TObject.NewInstance, TObject.NewInstace 调用 TObject.InitInstance,
最后又调用了 TObject.AfterConstruction.

我觉得李战这段话有点问题,现在我突然有个想法:不管是何 Class,只要
声明为 constructor 的函数,在以 Class 级别的调用中编译器就自动实现
以上函数的调用(包括分配空间、清零空间等),然后才是调用
TSomeClass.Create { 也就是用户建立对象时调用的函数 },Create调用完
成以后才由编译器再调用已生成对象的 AfterConstruction 。

如果是以 Object 级别的调用,或在 constructor 中调用 inherited,则
编译器实现为一般的函数调用。如 inherted Create, AObject.Create。

如果按照这个想法 TObject.Create 为空函数并没有错,不过李战说是 Borland
把 TObject.Create 内的函数删除了,然而确实 TObject.Create 在测试状态又
有代码,真是想不通。

如果按李战的说法由 TObject.Create 调用对象内存分配及初始化函数,那它
不能解释 TComponent.Create(AOnwer: TComponent); virtual 过程。

当然,我只是有这个想法,不知道 Borland 是不是这样实现的。
请高手指示。
 
->这时 TComponent.Create 如何能够调用 TObject.Create 方法呢?是不是
->Object.Create 方法一定会在任何类的 Constructor 函数之前自动调用。
->如果是这样,那么 TObject.AfterConstructor 应该是在 Constructor 方
->法之前调用,而这时(假设为TComponent) TComponent.Create(AOwner) 还
->没有执行,也就是说继承类还没有初始化之前就调用AfterConstructor,
->这显然是不对的。
在类的构造方法中其实传递了两个隐含的变量,一个是self就不说了,一个是
boolean类型的变量!它标志这个构造方法是由它的类方法调用还是通过inheride
来调用,并且在调用结束后会通过dl寄存器传递一个标志,标示是否要调用afterconstructor等后续过程。
这些事情都是编译器自动执行的,所以如果不深入delphi的内部是没法搞清楚的!
 
综合一下:
xuxiaohan 的: class constructor procedure Create; virtual;
加上
windbell 的: 类的构造方法中其实传递了两个隐含的变量 {Self 和 Boolean}
再加上
编译器自动执行的...

有意思,可是还是没搞懂编译器的实现 :(
我觉得 Borland 不会把事情搞得这么复杂吧...
我不是要钻牛角尖,只是在读源代码时实在是没有一气贯通之感,比如以上问题中所说的:
TThreadList.Create 中的 inherited Create。到底做了些什么事情?什么时候需要 inherited Create,什么时候不需要,原因又是什么....
 
TComponent并没有调用Tobject的构造方法。
实际上在Delphi中只要是构造函数在编译时就会自动调用到Tobject的NewInstance方法
而在NewInstance方法里面只有一行代码

Result := InitInstance(_GetMem(InstanceSize));

分配一块这个实例大小的内存并初始化。
所以楼主所说的是编译器处理好的的问题。而不是调用的问题
 
先谢谢各位的回答,还是希望有人能把 TComponent.Create 的编译器实现的代码写出来。
对此问题感兴趣的人帮忙提前。
 
我还是认为 Delphi 对象的创建应该是基于几条简单的规则,因为规则越多 Borland 设
计编译器反而更麻烦,另外 Borland 也要为第三方 VCL 开发商考虑。我认为 Borland
即使在对象的创建上隐藏什么,也不可能复杂到难以理解。
 
我的理解是:

constructor的参数除了类信息外,还有个标志,标志是否是被真正创建的时候调用还是被inherited调用。被真正创建的时候DL传入1,然后根据是否为1来调用classcreate的真正创建对象的过程。
TObject的create过程中判断如果DL是0就直接返回,对应着VCL源码中Create的啥都不做。其余的TComponent中判断DL是0就调用InsertComponent等过程,对应着VCL源码中的创建过程。TComponent.Create中是没调用TObject.Create过程的。但因为是构造函数,所以都会调用classcreate等内部过程。

[red]只要是类创建时的构造函数调用,入口时都会先调用classcreate过程来分配内存初始化对象。[/red]

至于为什么在构造函数中要增加一个DL来控制,就是为了避免在inherited Create过程的时候在父类和子类中重复调用两次创建对象分配内存初始化内容的代码。

 
至于要不要inherited create,主要看父类的构造函数的源码中有没有做你不能不做的事情。比如针对继承自TComponent的子类来说,因为父类TComponent的构造函数中实现了TComponent的父子关系,所以子类就不能不inherited。
TObject的Create啥都没做。所以直接继承自它的子类完全可以不inherted,也可以在这个子类中自己重新写一个构造函数而不用调用以前的Create。
 
Passion的分析非常透彻!
 
System不是一个类,而是一个单元
 
Passion 说得好,我开始明白 constructor 这个关键字的作用了。我现在的理解是:
AComponent := TComponent.Create(nil);编译器实现为:
System._ClassCreate;
TComponent.NewInstance;
TComponent.InitInstance;
TComponent.Create(nil);
TComponent.AfterConstruction;

Object Pascal Refrence 中的一段话很重要,它的实现可能就是 Passion 和 winbell 说的 DL 值检查:
When a constructor is called using an object reference (rather than a class reference), it does not create an object. Instead, the constructor operates on the specified object, executing only the statements in the constructor's implementation, and then returns a reference to the object. A constructor is typically invoked on an object reference in conjunction with the reserved word inherited to execute an inherited constructor.

xuxiaohan 说的也有道理:
class constructor procedure Create; virtual;
TObject.Create 应该是 virtual。否则 TObject 直属类能够调用 inherited Create 就讲不通了。不过为什么是 class 方法呢?可能是因为 TObject 上面再也没有祖先类,所以不可能 inherited 的缘故吧。

如果我们考虑 TThreadList 类:
TThreadList = class
private
FList: TList;
FLock: TRTLCriticalSection;
FDuplicates: TDuplicates;
public
constructor Create;
destructor Destroy; override;
procedure Add(Item: Pointer);
procedure Clear;
function LockList: TList;
procedure Remove(Item: Pointer);
procedure UnlockList;
property Duplicates: TDuplicates read FDuplicates write FDuplicates;
end;
注意它的构造函数声明为 constructor Create,没有使用 override, 却能 inherited Create。首先说明 TObject.Create 是 virtual。第二说明 Borland 为 TObject 继承类
的 Create 函数默认加了 override,甚至可能加了 class directive。不过这样程序员
就不好理解了。

以上是我根据大家的提示提出的想法,欢迎多多讨论。(我的汇编语言学得实在太差,否
则我就会将 object create 过程反汇编仔细看个清楚。)
 
to jackchin,
你说得对,就调用是 System 单元的函数 :)
 
能不能inherited与是否是virtual无关吧?
 

Similar threads

后退
顶部