近日在看李维老师的<<Inside VCL>>,又有了两点疑惑,以前看<<delphi面向对象编程思想>>也有疑惑,不过现在又有了,呵呵,只不过是看名家的书,但心中感觉有些问题
不说不快,疑惑1:书中57~58中,由于没有配套光盘,只抄了部分的代码
大概的代码:
TDerived = class(TBase)
private
  FData: Variant;
  
public
  ...
  function PureMethod: string;published
  function MyMethod1: string;
  procedure MyMethod2;
  property sData: Variant read FDate write FDate;
end;procedure TForm1.Button7Click(Sender: TObject);
var
  aObj: TDerived;
  sData: string;
  aPtr: Pointer;
begin
  sData := 'TDerived at ' + DateTimeToStr(Now);
  aObj := TDerived.Create(sData, HashOf(sData));
  ...
  ShowMethodAddress(aObj, 'MyMethod1');
  aPtr := ShowMethodAddress(aObj, 'MyMethod2');
  sData := MethodName(aPtr);   //注意这块
  sData := Format('%x : %s', [Integer(aPtr), sData]);
  Memo1.Lines.Add(sData);
  ....
end;function TForm1.ShowMethodAddress(aObj: TDerived; const sData: string): Pointer;
var
  aPtr: Pointer;
  sResult: String;
begin
  try
    aPtr := aObj.MethodAddress(sData);
    sResult := Format('%s : %x', [sData + '位于': ', Integer(aPtr)]);
    Memo1.Lines.Add(sResult);
  except
    on E: Exception do
    begin
      sResult := Format('%s : %s', [sData, e.Message]);
      Memo1.Lines.Add(sResult);
    end;
  end;
end;59页
书中有一段话
'有趣的是上面的程序代码可以通过调用MethodAddress取得published方法的地址,
但是对于其它种类的方法是无法取得方法地址信息的。如果我们反向调用MethodName
却无法取得某一地址的方法名称,这是有点奇怪的',59页又有一段代码
  procedure TForm1.Button8Click(Sender: TObject);
  var
    sData: string;
    aPtr: Pointer;
  begin
    aPtr := Pointer(@Self.OnClick);
    sData := MethodName(aPtr);
    sData := Format('%x : %s', [Integer(aPtr), sData]);
    Memo1.Lines.Add(sData);
  end;在60页
'因此我们可以推知MethodName似乎只会对从TComponent继承下来的类的published方法才有作用';这段话我感觉是有问题的,第一段代码之所以用MethodName取不到方法名,是因为在
TForm1.Button7Click(Sender: TObject)中直接调用sData := MethodName(aPtr);
相当于Self.MethodName(aPtr),也就是说调用TForm1的对象的MethodName
这当然找不到aObj的MyMethod2方法名,应该调用的是aObj.MethodName(aPtr);所以60页的这个结论不成立疑惑2:
93页中有个测试VMT表格创建的时机的代码.procedure TForm1.Button4Click(Sender: TObject);
var
  aPn1: TPanel;
begin
  aClass := aPn1.ClassType;
  sClassName := 'TPanel';
  ShowVMTContent(aClass);   //显示VMT内容的一个函数,不再抄了
  
  aPn1 := TPanel.Create(Self);
  aClass := aPn1.ClassType;
  sClassName := 'TPanel';
  ShowVMTContent(aClass);
  FreeAndNil(aPn1);
end;94书中说
'在未实际创建TPanel对象前我们经由aPn1显示VMT的内容,得到的结果应该是无意义的内容。然而一旦当TPanel对象实际在内存中创建之后就可以取得TPanel的VMT中的
内容.由这个现象来看,我们可以推知VMT应该是在第一个类对象被创建时才会创建完整的VMT内容,原因是属于同一个类的所有对象都是共享一个VMT表格'这话我又感觉有问题了,
  aClass := aPn1.ClassType;
  sClassName := 'TPanel';
  ShowVMTContent(aClass);
这个之所以是无意义的内容,因为aPn1本身是一个指针变量,一开始指向的就是无意义的内容,而ClassType的代码如下
function TObject.ClassType: TClass;
begin
  Pointer(Result) := PPointer(Self)^;
end;
所以这个取到的ClassType本身就是无意义的,
简单的来说
aClass := TPanel;
sClassName := 'TPanel';
ShowVMTContent(aClass);
这样就内容正确,这时并没有创建一个类的对象。
因此一个类的VMT创建的时机我觉得应该是在源码中第一次出现该类的名字时
创建的,换句话说应该是当编译器进行编译时发现你的源码中出现了TPanel这个词
那么就在内存中创建一个TPanel的VMT表.以上仅是自己的一点看法,并不定正确.书刚看了一点,继续看.

解决方案 »

  1.   

    我已经到china-pub买了这两本书,但现在还没收到,焦急等待ing。
      

  2.   

    whitetiger8([Einstein]):
    呵呵,主要是现在在家呆着,上网不是很方便
    所以不敢太多说话,呵
      

  3.   

    问题1、
    只有象我这样的菜鸟才会去翻帮助?还是我的理解力太差?恕菜鸟不知天高地厚、我怎么感觉是李维说了一大堆废话!If Address does not point to a published method of the object, MethodName returns an empty string.
      

  4.   

    疑惑1、
    对于第一个疑惑,就如楼上所言,MethodName只会返回一个published方法的类的函数
    疑惑2、
    每个类都是在Create执行时才分配内存地址空间的,不执行Create,当然得不到VMT
      

  5.   

    李维就XXX一个老太太一样,说话拐弯弯,所有‘湾湾’都这样,说直接点不好吗?如果李维不学‘唐僧’,不‘脱了裤子放屁’,楼主的疑惑就不会疑惑了!
    书的内容本身没错,拿那些例子得出一个结论就有点牵强了。
      

  6.   

    Create会调用一系列的函数和过程:
    function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
    class function NewInstance: TObject;
    function _GetMem(Size: Integer): Pointer;
    ......
      

  7.   

    1、
    “在60页
    '因此我们可以推知MethodName似乎只会对从TComponent继承下来的类的published方法才有作用';” 这个结论明显是错误的。其测试代码:
      procedure TForm1.Button8Click(Sender: TObject);
      var
        sData: string;
        aPtr: Pointer;
      begin
        aPtr := Pointer(@Self.OnClick);  //注意是取得Form1的点击事件处理方法指针,而此时Form1.OnClick处理方法根本不存在,自然返回空。如果书写了Form1.OnClick处理方法,自然可以正确返回其名字。
        sData := MethodName(aPtr);
        sData := Format('%x : %s', [Integer(aPtr), sData]);
        Memo1.Lines.Add(sData);
      end;
    2、“我们可以推知VMT应该是在第一个类对象被创建时才会创建完整的VMT内容,原因是属于同一个类的所有对象都是共享一个VMT表格”结论也是错误的,帮助上说清楚了:“  There is exactly one VMT per class (not one per object); distinct class types, no matter how similar, never share a VMT. VMTs are built automatically by the compiler, and are never directly manipulated by a program”。既然和类对应,自然不应该和具体的对象有关,实际上,VMT保存的都是一个类的任意个对象的共性的东西,也不需要依赖于具体的对象信息而存在。我看过此书第三章电子版,总的感觉是作者有些矫柔造作,一些简单的地方故意分割绕述,反而让人更不明白。比如第三章讲覆盖,按照inherited出现位置的不同,强制划分出“3明治”和“逐漸增加法”等,感觉实在牵强(我将这个意见转告给了此书的技术编辑者,作者回信在书中增加相关注释,不知是否付诸实施)。书的技术编辑基本也是形同虚设,光注重修改一些错别字和文句语法问题了,而需要的技术验证确抛掷一边。
    不过任何人的书都难免有错误,读者能有机会讨论清楚也就好了。————————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    ————————————————————————————————————
      

  8.   

    借地方打个广告:
    ‘今年你拿了多少年终奖?http://expert.csdn.net/Expert/TopicView1.asp?id=2656356’
      

  9.   

    关于procedure TForm1.Button7Click(Sender: TObject);中由  sData := MethodName(aPtr);   推出“如果我们反向调用MethodName却无法取得某一地址的方法名称,这是有点奇怪的”的结论,这是错误的,搂住已经将原因讲了,就是作者在这里实际调用的是Form1.MethodName。这是一个低级错误。作者似乎经常通过一些现象推论一些结论,这就是所谓格物致知吧!对此,我的一点感悟是:格物致知时一定要谨慎。不妨反过来想一下,Borland是否会让MethodAddress和MethodName出现如此不对称的现象,如果作者能不那么自信一些,考虑到此不对称现象应该不会出现,那么就能比较容易的推之是自己的错误,这样,书中自然也可以避免这个错误。
    至于接着的一个结论:
    “因此我们可以推知MethodName似乎只会对从TComponent继承下来的类的published方法才有作用”,我真的不知道是依据什么证据推得的。猜想可能与“类中没有显式指明作用域的成员,实际属于哪个域”的问题有关。比如:TForm1 = class(TForm)
      procedure Button1Click(Sender: TObject);
    private
    ……
    end;其中,Button1Click到底应该属于哪个区域呢?是private、public还是published抑或其他?我曾经听过作者的一堂课,他讲是private。但实际上并不是这样简单,正确的是:在$M编译指令开启时,属于published区域,否则是public区域。 而在VCL中,$M是在TComponent的父类TPersistent处开启的(可以看Classes单元源代码)。加上其他一些阴差阳错的即时联想,作者轻易作出了上述错误结论。————————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    ————————————————————————————————————
      

  10.   

    谢谢故国的论述.
    本来我非常期望读读李维老师的<<Inside VCL>>,不过现在有一点失望
    我看书有个坏习惯,如果书中出现一些明显的而不是印刷的错误,真的是没有
    心情读下去
    哎,总体这本书还是不错的,继续读下去.
    对了,你的书什么时候出版,期待早日见到.
      

  11.   

    楼上的,实际情况是这样的,类成员缺省的访问修饰符是 public,只当类的祖先类之一以 {M+} 编译指令编译时,类成员缺省的访问修饰符才是 published 的。编译器会为published 的字段,属性和方法生成RTTI 信息。Object Inspector 就是利用 RTTI 信息存取对象的published的属性。
    “因此我们可以推知MethodName似乎只会对从TComponent继承下来的类的published方法才有作用”应该改为“因此我们可以推知MethodName似乎只会对从TComponent 开始的类的published方法才有作用”
    还有,设计时IDE会将你赋给组件事件的事件处理过程的名字存到 dfm 文件中,dfm 文件作为资源链接到Exe 文件中,运行时 Form 创建时从相应资源中读出过程的名字,利用TObject.MethodAddress方法查找到过程的代码入口再赋给组件的事件指针(注意事件是指针)。因此将 Button1Click移到非published段,则编译器不会为它生成 RTTI 信息,用 MethodAddress 方法则无法在运行时还原事件指针,将会出错。
      

  12.   

    还有那个叫什么‘桂枝’的女人,出书是好事,但不要只是为了骗钱,如果叫老子5416写书,一定会物有所值,老子才是大中华地区的DELPHI泰斗!
      

  13.   

    楼上的,这只是小K了,老子还拿过‘亚洲论坛BBS灌军’!
      

  14.   

    http://expert.csdn.net/Expert/topic/2659/2659629.xml?temp=.7959253
      

  15.   

    5416(CSDN版5415) : 呵呵。“因此我们可以推知MethodName似乎只会对从TComponent 开始的类的published方法才有作用”实在是好笑得很。相比:
      {$M+}
      TMyClass = class
        procedure proc;
      end;
      {$M-}
    中,你不会认为TMyClass是TComponent或其子类吧,也不会糊涂到认为MethodName对proc不起作用吧?!不管你技术多么高明,看你满嘴喷粪就知道你的综合素质差的很。所谓“乱世用能人”,当今太平世界,即使你技术好,但是修养如此之低,怕也难有多大作为……————————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    ————————————————————————————————————
      

  16.   

    ‘桂花’,我是对DELPHI的VCL类库而言,你又扯到到那里去了呢?
    而且我用的是‘似乎’不是用的‘肯定’,不知道你上学的时间语文补考过几次?
    小女娃娃就真是没有教养!
      

  17.   

    ‘桂花’,
    哦,打错了字,‘桂枝’,
    我们这里谈的是DELPHI的VCL类库,仅VCL而言,你又扯到到那里去了呢?
    而且我用的是‘似乎’,不是用的‘肯定’,不知道你小时候上学,语文补考过几次?
    小女娃娃就是没有教养!
      

  18.   

    lxpbuaa(桂枝香在故国晚秋) 你的书叫什么名字到时候出了,通知一声,想叫你签个名再直接卖给我一本,我直接给你钱 :D
      

  19.   

    故国的书一定拜读
    5416(CSDN版5415):对于你这个'delphi届太抖',呵呵,我不知道说啥,
    我不说废话
    写了个例子,因为我不会写程序代码,写的乱,将就试一下unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, TypInfo;type
      TMyObject1 = class
        private
        FA: Integer;
        FB: Integer;
        FOnClick: TNotifyEvent;
        
      public
        constructor Create(AParent: TForm; ALeft, ATop: Integer);
      published
        MyButton1: TButton;
        procedure MyProc1;    property A: Integer read FA write FA;
        property B: Integer read FB write FB;
        property OnClick: TNotifyEvent read FOnClick write FOnClick;
      end;  {$M+}
      TMyObject2 = class
      private
        FA: Integer;
        FB: Integer;
        FOnClick: TNotifyEvent;
      public
        constructor Create(AParent: TForm; ALeft, ATop: Integer);
      published
        MyButton2: TButton;
        procedure MyProc2;    property A: Integer read FA write FA;
        property B: Integer read FB write FB;
        property OnClick: TNotifyEvent read FOnClick write FOnClick;
      end;
      {$M-}  TForm1 = class(TForm)
        Memo1: TMemo;
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure Button2Click(Sender: TObject);
      private
        { Private declarations }
        MyObject1: TMyObject1;
        MyObject2: TMyObject2;    procedure ShowInfo(AObject: TObject; const AM, AMethodName, AFieldName: string);
      public
        { Public declarations }
      end;var
      Form1: TForm1;implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
    begin
      ShowInfo(MyObject1, '{$M-}', 'MyProc1', 'MyButton1');
    end;{ TMyObject }constructor TMyObject1.Create(AParent: TForm; ALeft, ATop: Integer);
    begin
      MyButton1 := TButton.Create(AParent);
      MyButton1.SetBounds(ALeft, ATop, 120, MyButton1.Height);
      MyButton1.Parent := AParent;
    end;procedure TMyObject1.MyProc1;
    begin
      ShowMessage('TMyObject1.MyProc1');
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      MyObject1 := TMyObject1.Create(Self, 0, 0);
      with MyObject1.MyButton1 do
      begin
        MyObject2 := TMyObject2.Create(Self, Left + Width + 2, Top);
        Memo1.SetBounds(Left, Top + Height + 2, Width * 2, Memo1.Height);    Button1.SetBounds(Left + Width * 2 + 2, Top, Button1.Width, Button1.Height);
        Button2.SetBounds(Left + Width * 2 + 2, Top + Button1.Height, Button1.Width, Button1.Height);    Self.SetBounds(Self.Left, Self.Top, 2 * Width + Button1.Width + 10,
                       Height * 3 + Memo1.Height);
      end;
      Memo1.Lines.Clear;
    end;procedure TForm1.FormDestroy(Sender: TObject);
    begin
      MyObject1.Free;
    end;procedure TForm1.ShowInfo(AObject: TObject; const AM, AMethodName, AFieldName: string);
    var
      PMethod, PField,PInfo: Pointer;
      PropList: PPropList;
      I, PropCount: Integer;
    begin
      Memo1.Lines.Add('======='  + AObject.ClassName + ' ' + AM + ' =======');
      PMethod := AObject.MethodAddress(AMethodName);
      Memo1.Lines.Add(AObject.ClassName + '.' + AMethodName + '方法的地址: $' +
                     IntToHex(Integer(PMethod), 8));
      Memo1.Lines.Add('地址$' + IntToHex(Integer(PMethod), 8) + '的方法名:' +
                       AObject.MethodName(PMethod));
      PField := AObject.FieldAddress(AFieldName);
      if PField <> nil then
      begin
        Memo1.Lines.Add(AObject.ClassName + '.' + AFieldName + '字段的地址: $' +
                        IntToHex(Integer(PField), 8));
        TButton(PField^).Caption := AObject.ClassName + '.' + AFieldName;
      end;  PInfo := AObject.ClassInfo;  if PInfo = nil then
        Memo1.Lines.Add(AObject.ClassName + ' 无类型信息')
      else
      begin
        Memo1.Lines.Add(AObject.ClassName + ' 有类型信息');
        PropList := nil;
        PropCount := GetPropList(PInfo, PropList);
        Memo1.Lines.Add(AObject.ClassName + ' 有' + IntToStr(PropCount) + '个属性: ');
        for I := 0 to PropCount - 1 do
          Memo1.Lines.Add('    ' + PropList[I].Name);
        if Assigned(PropList) then
          FreeMem(PropList);
      end;
    end;
    { TMyObject2 }constructor TMyObject2.Create(AParent: TForm; ALeft, ATop: Integer);
    begin
      MyButton2 := TButton.Create(AParent);
      MyButton2.SetBounds(ALeft, ATop, 120, MyButton2.Height);
      MyButton2.Parent := AParent;
    end;procedure TMyObject2.MyProc2;
    begin
      ShowMessage('TMyObject2.MyProc2');
    end;procedure TForm1.Button2Click(Sender: TObject);
    begin
      Memo1.Lines.Add('');
      ShowInfo(MyObject2, '{$M+}', 'MyProc2', 'MyButton2');end;end.分别用两个TMyObject1在{$M-}下,TMyObject2在{$M+}下
    在$M无论是开还是关的情况下,MethodAddress, MethodName, FieldAddress
    都取到正确的值
    在{$M-}下,TMyObject1的ClassInfo为nil, 也就是它的类型信息为空,取不到该类的属性信息
    在{$M+}下,TMyObject2的ClassInfo不为nil,可以取到该类的属性信息在delphi帮助中
    ass is declared in the {$M-} state, and is not derived from a class that was declared in the {$M+} state, published sections are not allowed in the class.
    我英语不太好
    我现在用的版本是delphi6,是不是以前的版本
    在{$M-}状态下,类中不允许出现published段
      

  20.   

    抱歉
    少写了MyObject2.Free;
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      MyObject1.Free;
      MyObject2.Free;
    end;
      

  21.   

    我来说句公道话,老达摩的这个例子其实上面5416已经说的很清楚了,可能只是有一点Bug,呵呵,对事不对人
      

  22.   

    FrameSniper(§恋爱的味道是甜的§):
    是啊,好久不见了,还好吧,我也刚看这书不久.
    5416:
    我确实听不懂,没有办法,
    不过看你这张爱说话的嘴,怎么看都不像是搞程序的
    算了,我不爱和别人练嘴,我看书去了
    结贴了