type 
  TMyClass = class(TObject)
     constructor Create;
  end;var
  c: TClass;
begin
  c := TMyClass;  // 如何通过变量c,获取TMyClass的构造函数地址呢?
  // c.Create只会TObject的构造函数,不会调用到TMyClass的构造函数end; 

解决方案 »

  1.   

    只会TObject的构造函数 -> 只会调用到TObject的构造函数
      

  2.   


      Tfoo = class
      public
        constructor bar1;
        constructor bar2; overload;
        constructor bar2(foofoo: Integer); overload;
      end;1. Tfoo 有好几个 constructor,应该得到哪个?
    2. bar1 和 bar2 都不是虚方法,凭什么出现在 vmt 中?
    你的 MyClass 在调用 TMyClass.Create 的时候根本不需要查 vmt
      

  3.   

    to Seamour:1、如果DELPHI有能力获取overload的就更好了,不行的话可暂不考虑。
    2、你说的对,我忽略这个问题了。不过其实我的意思是,是否有办法可以调的到呢?如果对AClass进行手工转型,如TMyClass(AClass).Create,是可以调用到的,但是不转型就不行。现在我想设计通用的程序,所以不可能进行手工转型。因此想看看有没什么方法可以解决这个问题。PS. 我一直都在怪TOBJECT的构造函数为什么不声明成虚函数,嫌virtual占空间多的话用dynamic也行嘛。
      

  4.   

    首先,vmt 只是个表,编译器也只是在符号表中记录了每个虚函数在 vmt 中的偏移量,你想通过什么从 vmt 中获取?函数名字符串么?为什么不声明成虚函数其实我已经回答你了,对于对象的设计来说,问题1远比问题2重要的多。正是因为 constructor 有不同的参数,而不像 destructor 那样什么参数都不需要并且必须是虚函数(逻辑必须上绕过父类清理的情况是极少的),所以会有 vmtDestroy 而不可能有 vmtCreator。比如 vcl 控件的 constructor 都是 Create(AOwner: TComponent) 的形式,你在 TObject 里给它一个 Create; virtual; 不但浪费空间,还一点儿用都没有。不过这些还都是小事,更糟的是用户想都不会想上来就 TButton.Create,编译器一声都不会吭,等到程序挂了的时候你就慢慢找吧。从类的封装来讲,某一些组类如果需具有相同的逻辑,可以使用形式相同的构造函数的话,那么继承自一个共同的基类会省去很多麻烦。只要在基类中声明一个虚构造函数,在子类中继承再写代码就完了。从你的需求来看,只要在 TMyObject 中声明一个 constructor Create; virtual;,再来个 TMyObjectClass = class of TMyObject 的声明就够了,非要用 TClass 不是跟自己过不去么。
    多说一句,你的 TMyClass 并不是一个好的命名方式,按照 delphi 的命名习惯,容易让人认为 TMyClass = class of TMy,或者只是一个不需要实例化的函数包
      

  5.   

    to Seamour:谢谢你的回答。其实TObject声明成虚构函数真的没什么,子类可以reintroduce的。至于为什么不自己再封装呢?我肯定有我的理由。OOP我想我还是懂的。我说了我是在做通用的东西,所以才必须用TClass,就象你会把很多方法的接口参数声明成TObject,然后用多态一样。
      

  6.   

    首先,vmt 只是个表,编译器也只是在符号表中记录了每个虚函数在 vmt 中的偏移量,你想通过什么从 vmt 中获取?函数名字符串么? ------------------------------我要正确调用子类中非虚函数的构造函数。
      

  7.   

    非虚函数你怎么从类的信息中获得?都是直接跳绝对地址的,除了大量的 case/if 外没办法实现从 oo 的角度说,你不要指望给不是自己设计的东西非常容易的加上“通用性”。至于 TClass,恐怕除了枚举类名之类的情况外,我的代码中是从来不会出现的。
    多说一句,c 系列语言的构造函数都没法是虚函数,cpp 还有看起来“更过分”的限制连在构造函数里调用虚函数都不行,这些你都想过为什么吗?delphi 能提供虚函数的构造函数就已经很不寻常了,不是只有想不到没有做不到
      

  8.   

    非虚函数你怎么从类的信息中获得?都是直接跳绝对地址的
    --------------------------------------------------
    我一开始问的时候也是问地址
    从 oo 的角度说,你不要指望给不是自己设计的东西非常容易的加上“通用性”。至于 TClass,恐怕除了枚举类名之类的情况外,我的代码中是从来不会出现的。
    ----------------------------------------------------
    TClass太常用了,FindClass一个例子
    多说一句,c 系列语言的构造函数都没法是虚函数
    ----------------------------------------------------
    这个有所理解。不过其实你也要明白,DELPHI的构造函数也很特殊,它其实只是一个普通的函数。从类引用上调用构造函数,和从对象引用上调用构造函数,是不一样的,足以看出这点。
      

  9.   

    ……
    你要是懂汇编的话,我也不用解释了,自己就该明白都是怎么回事了直接地址的意思是,比如TA = class
      constructor CreateA;
    end;TAClass = class of TA;TB = class
      constructor CreateB; virtual;
    end;TBClass = class of TB;  TAClass(TA).CreateA; {1}
      TBClass(TB).CreateB; {2}1生成的代码是
     call $004abcdefg
    这是个地址在编译期就决定好了的,也就是个常数。对于每个不同的class来说,这个常数都是不一样的。你有100个 class 就得写100个 case 才能“通用”,而且每引入一个 class 还得手动往里加2生成的代码是
      mov eax, [TB]
      call [eax + VMT_Offset_of_CreateB]
    也就是说,要根据该 TBClass 指向的 vmt 中某一偏移量的地址决定调用什么代码
    除非要使用 vcl 的固化机制,不然我不用 FindClass/GetClass 这种既没效率还得写代码的东西
    constructor 也没啥特殊的,只是有个隐含参数而已。delphi 传隐含参数的地方太多了,这要是特殊的话,那 function foo: string 也特殊,dynamic 方法也特殊,procedure foo(x: array of T) 也特殊……平时写的东西里就没几个不特殊的了。
      

  10.   

    我也试过跟踪汇编,我的问题就是有没方法拿到这个地址而已,如果不行那也没办法了。也不是100个class就有100个地址,得看子类是否有重新声明构造函数,否则只是CALL父类那个而已。关于FindClass和GetClass就不讨论了,但是这种机制在OOP里是很经常会用到的,我不知道你是否有体会了。不过这个不是本帖讨论点。
      

  11.   

    答案当然是没有了,暴力列举倒是应该能找到所有的 constructor 的地址,但是没法和 class 对应起来不用 FindClass/GetClass 是因为我有自己的机制,vcl 的读写还是挺麻烦的,暴力改 TReader/TWriter 也应该能做到,但是懒得自己再实现一个了。而且说到这儿我想起来了,FindClass/GetClass 应该只限于 TPersistent,而不是 TClass,这也是我不用它的原因。
      

  12.   

    这个地址和类的地址难道就完全没相关性吗?例如说是否存在一定的偏移量等?现在既然有TClass的变量传入,说明类的地址是有的了。
      

  13.   

    从TObject派生这样是不合适的.
    VCL系统中典型的模式是TComponent及其派生类.
    关键在于TComponent的构造函数是虚函数.
    所以楼主想要用这种设计模式不应该从Tobject派生.而是应该创建属于自己的一个基础类.有它自己的虚构造函数.这样从它派生的子类就可以享受这样的待遇了.type
      TMyBase = class(TObject)
        constructor Create; virtual;
      end;  TMyBaseClass = class of TMyBase; //这里应该用类类型  TMyClass = class(TMyBase)
         constructor Create; override;
      end;{ TMyClass }constructor TMyClass.Create;
    begin
      Inherited Create;
      OutputDebugString('TMyClass.Create;');
    end;{ TMyBase }constructor TMyBase.Create;
    beginend;var
      c: TMyBaseClass;//这里应该用类类型
      cc : TMyBase;
    begin
      c := TMyClass;
      cc := c.Create;
    end;
      

  14.   

    to #18:不是函数和参数,是对象和方法。其实也是,如果那个地址是编译时从一个类似地址表的地方填进去的话,那就无法获取了。我的思维之前还是停留在对象元信息的角度来考虑问题,所以说对象和方法的元信息(地址也可以算是个信息吧)有相关性是合理的。to #19:
    我完全明白你说的方法。不过我的框架需要解决动态调用构造函数的问题。
      

  15.   

    纯函数没有对象的多态、继承特性,多态才是OOP的精粹。如果只是认为对象等于把属性和方法封装起来的话,那不算面向对象,只能算是使用对象。
      

  16.   

    貌似Harryfin老大想实现IoC中的构造函数注入?:)
    在D2010里面用反射就可以了,在之前的版本还没有办法
      

  17.   

    哈哈,不同的开发平台特性不同,我在想,是不是可以再简单点,比如:
    Container.RegisterService(TypeInfo(IService), TypeInfo(TService));
    Container.InjectConstructor(TypeInfo(TService), @TService.Create, ['123456', 789]);P.S. 不过咋一看这种方法好像不支持overloaded的constructor
      

  18.   

    我觉得你应该先搞清楚自己要干嘛,oo只是结构化程序设计的一个模型而已。你现在要搞的东西是 delphi 的 oo 模型里没给你的,所以你眼里的 object 应该是一块数据、一堆指针、一些毫不相关的函数和一块 rtti 而已,而不是一个黑盒子。
    话说回来,虚函数/继承也只不过就是个函数指针而已,根本没有你所说的没有多态、继承的问题。oop 只是在语法上作出规定,然后由编译器维护 rtti 之类的东西而已。
      

  19.   

    另外还有一个可以专门对付用 T*Class.constructor 创建类时的办法,自己重写一个 system._ClassCreate 然后把 sytem 单元的给替换掉
    还有一种办法,可以写个 memory manager,只写 GetMem 的部分就够了,除了调用原来的 GetMem 外,再判断一下是否是由 TObject.NewInstance 调用的,如果是的话,ebx 就是类指针
      

  20.   

    为所有有这种需求的类建立一个基类,将其构造函数声明称虚拟的。就可以解决问题了。
    当然,就不能c: TClass了,而应该c: class of TBase;
      

  21.   

    请教下Seamour,我是否可以人工用汇编来调整AClass.Create的执行框架呢,我汇编方面不是很熟。因为之前也说了,AClass.Create是调不到重新声明的构造函数的,但是TMyClass(AClass).Create的话是可以的
      

  22.   

    我也想请教下Seamour大侠,如何获得有重载的类方法的地址?
      

  23.   

    D2010可以获取一个类定义的所有构造函数,问题在于不能获取哪些是合法的(overloaded)
      

  24.   

    不能,只能动 system._ClassCreate 之类有唯一代码地址并能取到的。前面已经说了,constructor 只是个有隐含参数的函数而已,你有办法动一个连地址都不知道的函数的执行逻辑么?已经说了很多遍了,那些面向对象的东西都已经是编译器封装好了之后你看到的,不要认为它们有天然的联系。如果 rtti 里没提供的话根本就没办法从其它途径知道没看明白你的意思……是说,假如有  Tfoo = class
        procedure bar; overload;
        procedure bar(V: Integer); overload;
      end;如何获得这两个 Tfoo.bar 的地址么?
      

  25.   

    是的。@TFoo.bar应该只是其中一个方法。如何获得这两个方法并区分?
      

  26.   

    重载的函数在编译器的符号表中应该是同一个名称的,只有通过参数匹配的方式才能匹配其中的特定项,否则只要找到第一个匹配项就直接返回了。所以按照我的理解,关键的地方还是类型匹配,有没有更简单的办法就不知道了:procedure foobar1;
    type
      TBar0 = procedure of object;
      TBar1 = procedure(V: Integer) of object;
    var
      method1, method2: TMethod;
      foo: Pointer;
    begin
      foo := Tfoo;
      TBar0(method1) := Tfoo(@foo).bar;
      TBar1(method2) := Tfoo(@foo).bar;
      { method1.Code = procedure Tfoo.bar }
      { method2.Code = procedure Tfoo.bar(V: Integer) }
    end;procedure foobar2;
    var
      foo: Pointer;
      bar0: procedure of object;
      bar1: procedure(V: Integer) of object;
    begin
      foo := Tfoo;
      bar0 := Tfoo(@foo).bar;
      bar1 := Tfoo(@foo).bar;
      { @bar0 = procedure Tfoo.bar }
      { @bar1 = procedure Tfoo.bar(V: Integer) }
    end;需要说明的是,中间变量 foo 是为虚方法或动态方法服务的,普通成员函数直接用 nil 就够了。