《Inside VCL》中的一个问题(100分)

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

savetime

Unregistered / Unconfirmed
GUEST, unregistred user!
第二章最后一页。我注意到其中一句话:“类的vmt内容在第一个类对象创建时才会建立在内存中”。我当时的反应是:怎么可能?
李维举的例子好像是这样的:
var
AObject: TSomeObject;
AClass: Pointer;
begin
AClass := AObject.ClassType;
ShowVmtInfo(AClass);
AObject := TSomeObject.Create;
AClass := AObject.ClassType;
ShowVmtInfo(AClass);
end;

因为两次显示的vmt信息不同,所以李维下此结论。在此截取前几天我对类方法调用的汇编分析的代码:
var
Com: TComponent;
Str: String[255];
begin
Str := Com.ClassName;
lea edx, [ebp-$00000104]
mov eax, [ebp-$04]
eax = pointer to object
mov eax, [eax]
eax = pointer to VMT
call TObject.ClassName

可以看到,以对象调用类方法的时候,编译器生成的汇编代码仍然是以对象的地址查询到VMT指针的。所以李维的例子中,第一次调用 AClass := AObject.ClassType 并不能找到VMT的地址(因为对象还没有初始化),所以结论自然是错误的。我相信VMT的地址和内容在程序执行时就已经确定了。李维会得出这样的结论是不是因为受了C++的影响?(懂C++的人可以回忆一下local static object的内存地址和构造函数的调用时机,是不是与李维的想法有点类似?)

为了测试VMT是否在类的第一个对象创建前就存在,可以把李维的例子稍作修改,把对象方式的引用改为类引用:
var
AObject: TSomeObject;
AClass: Pointer;
begin
AClass := TSomeObject.ClassType
// 把原来的 AObject 改为 TSomeObject
ShowVmtInfo(AClass);
AObject := TSomeObject.Create;
AClass := AObject.ClassType;
ShowVmtInfo(AClass);
end;
我没有测试这个例子,有配书光盘的人可以试一下。
 
我只是看了第一章节的内容,后面还没看,如果你需要这个例子,我发给你
antic_ant@163.com
 
谢谢 antic_ant 我的邮箱 savetime2k@yahoo.com
 
我给你发的邮件,好象都是乱码。
我的光盘在家呢,明天带来发给你
 
我并不十分佩服李维,但这一点他说得没错。楼主理解错了
 
》类的vmt内容在第一个类对象创建时才会建立在内存中
我认为是没问题的呀,
savetime兄终于拿到书了?[:)]
 
to antic_ant,
我忘记了Yahoo邮箱对中文处理的问题,谢谢你!

to wr960204,
是吗?我还是动手试一下吧。感谢你提出的看法。
 
to qince,
我订货的书店把书卖光了没通知我,把我气的。
我昨晚在另一家书店看到了这本书,看了一个小时,感觉和我前几天读源码学的差不多,刚好一个同事要买这本书我就没买。我不太喜欢看电子版,所以原来没有注意到这个问题,昨天在书店看书的时候看到了,就写了下来。
你们都认为李维说的没错,那么很可能是我错了,我先测试一下吧。
 
我测试李维的代码,执行第一句就出现访问冲突异常,原因是我上面说的对象没有初始化:
procedure TForm1.Button1Click(Sender: TObject);
var
aPnl: TMemo;
begin
aClass := aPnl.ClassType;
sClassName := 'TPanel';
ShowVMTContent(aClass);
aPnl := TMemo.Create(Self);
aClass := aPnl.ClassType;
sClassName := 'TPanel';
ShowVMTContent(aClass);
FreeAndNil(aPnl);
end;

我在楼顶说的测试方法也有错,因为 TypeInfo 不是类方法,是我记错了。但是这不影响对问题的讨论。我昨天只是翻阅了一下这本书,楼顶的李维的代码是凭记忆写的。我以为李维将 aClass 声明为 Pointer,没想到是声明为 TClass,这就更好办了。

我把测试代码改为以下,没有在表单上放置 TMemo 控件。测试的结果是两次显示的 VMT 信息都一样。

procedure TForm1.Button1Click(Sender: TObject);
var
aPnl: TMemo;
begin
aClass := TMemo;
sClassName := 'TPanel';
ShowVMTContent(aClass);
aPnl := TMemo.Create(Self);
aClass := aPnl.ClassType;
sClassName := 'TPanel';
ShowVMTContent(aClass);
FreeAndNil(aPnl);
end;

目前来说我还是相信我的判断:即,VMT内容是在应用程序执行之初就确定的,与对象是否创建无关。但是.... 晕了,我已经不知道问题出在哪里了。

我把电子版中的李维的结论拷贝在这里,帮我看看到底是怎么回事?

《Inside VCL》第二章内容:
图2-22 类的VMT 内容在第一个类对象建立时才会具体的建立在内存中

上面的程序代码首先声明了一个区域TPanel 变量aPnl ,接着在还未实际
建立TPanel 对象之前我们藉由aPnl 显示VMT 的内容,得到的结果应该是无意
义的内容。然而一旦当TPanel 对象实际在内存中建立之后就可以取得TPanel
的VMT 中的内容。由这个现象来看,我们应该可以推知VMT 应该是在第一个
类对象被建立时才会建立完整的VMT 内容,因为属于同一Java 类的所有对象
都是共享一个VMT 表格。
 
请问这本书哪里可以下载到?谢谢。
 
to Corn1,
好像在 CSDN 中可以下载目录及前两章,你自己找找吧。
 
问题是我需要的是整本书啊,唉,不知道什么时候才会有电子版。
 
to Corn1,
书店有,真的是本好书,我昨晚在书店看了一个小时,发现了很多重要的观念。
我是因为坐在旁边的同事要买一本,所以才没买。
 
再把问题明确一下:

李维说:“类的VMT内容在第一个类对象建立时才会具体的建立在内存中”

也就是说对于 TMemo 类,如果没有创建 TMemo 类的对象(如 Memo1 := TMemo.Create(nil)),
那么TMemo类的 VMT 内容是不确定的,因为它没有[具体地建立在内存中]。

我的观点是:类的 VMT 内容,包括 VMT 地址,是在应用程序被操作系统从 EXE 文件中加载
完成后,就已经初始化了。与类的对象(Memo1)是否创建无关。


再来看看李维的例子:

var
aPnl: TMemo;
begin
aClass := aPnl.ClassType
-> 李维试图通过一个没有初始化的对象来获得类的VMT
这是不可行的,原因在楼顶已经说明
ShowVMTContent(aClass)
-> 显示错误的 VMT 内容,因为 VMT 指针是错误的
aPnl := TMemo.Create(Self)
-> 然后李维建立了一个对象实体
aClass := aPnl.ClassType
-> 然后通过对象访问类的 VMT 指针,是可行的
ShowVMTContent(aClass)
-> 显示正确的 VMT 内容
FreeAndNil(aPnl)


李维以此说明他的观点:类的VMT内容在第一个类对象建立时才会具体的建立在内存中

由于李维的测试方法是错误的,我的测试方法如下:
procedure TForm1.Button1Click(Sender: TObject);
var
aPnl: TMemo;
begin
aClass := TMemo
-> 因为 aClass :TClass,所以这句的意思就是
aClass 是指向 TMemo 的 VMT 的指针
ShowVMTContent(aClass)
-> 显示正确的 VMT 内容,注意,这时并没有创建 TMemo
对象实体
aPnl := TMemo.Create(Self)
-> 创建一个 TMemo 对象的实体
aClass := aPnl.ClassType
-> 通过 ClassType 获得 VMT 指针
ShowVMTContent(aClass)
-> 得到的结果与创建 TMemo 对象之前相同

希望我这次把问题说清楚了。
 
没看过书,我认为你的观点是正确,不要太迷信李维。
aPnl是个没初始化的局部变量为随机值,怎么可能得到VMT.
类的VMT信息肯定编译到Exe或Dll或bpl里了,它随着PE文件被加载而映射到内存的静态区,生存期贯穿进程的始终.
 
(咦,怎么没有收到 xeen 发贴的邮件通知,幸好我上来看了一下。:)
终于有人同意我的看法了,好,看看还有没有不同的意见?
 
你去跟踪一下连续两个同一类对象的创建过程的汇编代码吧,我没看到有任何不同。
用事实说话[:)]
 
to xeen,
我虽然完整地跟踪过一个对象的构造过程,但是其中调用的汇编代码太多,还有些汇编
段落没看懂,所以不敢肯定地说同一类的两个对象的创建过程完全相同。这也是在此提
问的原因之一吧。
但是,我有 95% 的把握同意你的说法。
 
后退
顶部