怎么样才能写出一个像TObject类CObject来?

caimouse
人工智能领域优质创作者
博客专家认证
2002-12-04 07:42:00
我看了C-VIEW那几篇文章后,总想写一个TObject类来,
还看了一下PASCAL源程序,
但还是写不出来.
发现下面两个方法不一样的:
TObject* pt = new TObject();//VCL的类.
CObject* po = new CObject();//自己写的类.
它们的构造过程序不一样,
我查看了它的源程序ASM,
TObject多了很多东西,根本没有用NEW操作符.
...全文
189 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
caimouse 2002-12-11
  • 打赏
  • 举报
回复
改写不是我的目的,只是我作研究一下VCL的实现,就相当于<<深入浅出MFC>>
差不多.想模拟一个TOBJECT类出来.
并且如果把这个可视化的技术放到我们的软件里也是一个好事情.
阿发伯 2002-12-10
  • 打赏
  • 举报
回复
m = *new MyClass; 或 m = new MyClass;
掉了两个()
阿发伯 2002-12-10
  • 打赏
  • 举报
回复
对这个问题谈一些浅见。
首先,我认为要写一个与TObject完全相似的类是不可能的!
因为TObect是Object Pascal中唯一的基类,也是Object Pascal的核心支柱,没有TObject就没有Object Pascal,就没有VCL, 也就没有现在的Delphi,Borland C++也不会演变成Borland C++ Builder。因此TObject只是在外形上像C++类,而底层的东东完全是两码事:
TObject是靠Delphi或者C++ Builder编译器支撑的唯一的VCL基类,而C++可以任意写一个CObject、BObject等类。这两种类是不相同的,在Delphi中,
MyClass = class 等同于 MyClass = class(TObject),
而C++ Builder的
class MyClass 不等于 class NyClass : public TObject。
在C++ Builder中可以这样写:

class MyClass
{
int Value;
...
};

MyClass m;

m.Value = 0;

而不能这样写:

class MyClass :public TObject
{
int Value;
...
};

MyClass m;

m.Value = 0;

后者必须显式的创建类事例后才能使用(还需显式的释放):
MyClass& m; MyClass* m;
m = *new MyClass; 或 m = new MyClass;
m.Value = 0; m->Value = 0;

这就说明了基于TObject的类只能动态创建,而其它C++类既可动态创建,也可静态定义。那么:
TObject* pt = new TObject();//VCL的类.
CObject* po = new CObject();//自己写的类.
不同是必然的。要它们相同也是不可能的!!!

其次,如果想学TObject的管理机制,并非只是VCL,底层的VCL中既包含Windows API,也包含ASM,相当复杂。早期的Borland产品,如Trubo C++3.0、Turbo Pascal 7.0有一个基于DOS的Visual类库,只有三个完全的汇编文件,其余完全是C++或Pascal,TObject就在其中,注意,这个TObject可是VCL中TObject的老祖宗。
child_bj 2002-12-10
  • 打赏
  • 举报
回复
你是不是想用C++改写VCL?

看你上面的问题,如果写一个自己的组件,就要从根类写起,因为BCB的组件也是一层一层继承下来的。如果你只改写了最下面的,实际上你如果继承自其他VCL类,则根本没什么意义。如果你全部自己写不继承VCL,那会失去很多东西,想把这些东西都自己写的话~~~工程太大了。

还有,说起用C++改写VCL,我觉得,那真的好真的有必要吗?真的改了,是不是大家还得有一个习惯的过程?还不如去学PASCAL来的快吧。
阿发伯 2002-12-10
  • 打赏
  • 举报
回复
CBuilder的可视化组件是建立在VCL基础上的,开发一个不属于VCL的组件进行可视化编程是不可能的。但是,设计几个类似VCL组件的类是完全可行的,但是不能挂在组件板上,只能非可视化调用。这方面的资料也不少,不就是Windows API吗!
caimouse 2002-12-10
  • 打赏
  • 举报
回复
还有一个问题是如果不继承所有VCL类,能不能自己写一个组件能在cbuilder
上,跟原来的组件一样?
或者是知道CBUILDER的接口,然后用c++写完cbuilder要的函数,
我想这样应是可以实现的。
但是缺少这资料。
大家有吗?
caimouse 2002-12-10
  • 打赏
  • 举报
回复
其实我也看了过TOBJECT 的源码,虽然是汇编写的,
但它是从PASCAL编译成汇编后,才人工修改的。
既然c++builder6.0后,支持移值到linux上,看了一下它的CLX好像差别不大。
linux那里面没有win api,那它是怎么样把linux的调用放进去呢?
极其有可能的就是borland已经把TObject写死在编译器里了。
就算你看到它的VCL库的源码,也没有办法另作它用。


http://www.codediy.com/codebbs/index.asp
Lewolf 2002-12-09
  • 打赏
  • 举报
回复
to: invalid(空心菜之2.0开发中)

问世间谁是真的大英雄,
谁是大英雄
谁是大英雄…………
caimouse 2002-12-09
  • 打赏
  • 举报
回复
好像上面只是构造了一个跟TOBJECT内存结构有点像的对象,
但如果要用NEW出来的对像,就跟TOBJECT一样,看来是不一样的。
上面的东东,我也试过。
TObject* pt = new TObject();//VCL的类.
CObject* po = new CObject();//自己写的类.
如果能实现这样的办法的话,应才算完全实现了。
child_bj 2002-12-09
  • 打赏
  • 举报
回复
呵呵,全部来自虫虫的《天方夜谭VCL: 开门》
更详细的文章你可以在
http://www.c-view.org/journal/003/vcl_chong.htm#text
找到

我们仿造TObject,写一个类FObject(呵呵,如果把TObject看成True Object,我们的FObject就是False Object)。要问下面这段代码从哪里来?大部分都Copy&Paste自Include\Vcl\systobj.h文件。

class FObject
{
public:
FObject(); /* Body provided by VCL {} */
Free();
TClass ClassType();
void CleanupInstance();
void * FieldAddress(const ShortString &Name);

/* class method */
static TObject * InitInstance(TClass cls, void *instance);
static ShortString ClassName(TClass cls);
static bool ClassNameIs(TClass cls, const AnsiString string);
static TClass ClassParent(TClass cls);
static void * ClassInfo(TClass cls);
static long InstanceSize(TClass cls);
static bool InheritsFrom(TClass cls, TClass aClass);
static void * MethodAddress(TClass cls, const ShortString &Name);
static ShortString MethodName(TClass cls, void *Address);

/* Hack: GetInterface is an untyped out object parameter and
* so is mangled as a void*. In practice, however, it is
* really a void**. Be sure when using this method to provide
* two levels of indirection and cast away one of them.
*/

bool GetInterface(const TGUID &IID, /* out */ void *Obj);

/* class method */
static PInterfaceEntry GetInterfaceEntry(const TGUID IID);
static PInterfaceTable * GetInterfaceTable(void);

ShortString ClassName()
{
return ClassName(ClassType());
}

bool ClassNameIs(const AnsiString string)
{
return ClassNameIs(ClassType(), string);
}

TClass ClassParent()
{
return ClassParent(ClassType());
}

void * ClassInfo()
{
return ClassInfo(ClassType());
}

long InstanceSize()
{
return InstanceSize(ClassType());
}

bool InheritsFrom(TClass aClass)
{
return InheritsFrom(ClassType(), aClass);
}

void * MethodAddress(const ShortString &Name)
{
return MethodAddress(ClassType(), Name);
}

ShortString MethodName(void *Address)
{
return MethodName(ClassType(), Address);
}

virtual HResult SafeCallException(TObject *, void *);
virtual void AfterConstruction();
virtual void BeforeDestruction();
virtual void Dispatch(void *Message);
virtual void DefaultHandler(void* Message);

private:
virtual TObject* NewInstance(TClass cls);

public:
virtual void FreeInstance();
virtual ~FObject(); /* Body provided by VCL {} */
};

当然FObject::ClassType我们已经会写了,那就是
TClass FObject::ClassType()
{
return *(TClass*)this;
}

我们会在后面陆续把这些成员函数填充完整。先举个例,拿类名(ClassName)开刀吧。
查查VMT表,vmtClassName = 0xffffffd4,我们就从这里下手。主要的步骤是:

找到VMT的入口;
通过vmtClassName找到储存类名的地址;
获取类名。
0xffffffd4也就相当于– 44,也就是VMT入口指向的地址开始,倒数第44字节到倒数第41字节这4个字节所代表的指针,指向类名。假设入口指向的地址是cls,那么vmtClassName所代表的地址就是(char*)cls – 44,亦即(char*)cls + vmtClassName。

注意一个字符串格式的问题,VCL既然是用Object Pascal写的,其中储存类名的字符串的格式必然是Pascal传统方式,也就是第1个字节为字符串的长度,紧接着为字符串的实际内容。在C++ Builder中,与之对应的类型是ShortString。



图5 TObject::ClassName的运作方式

代码如下:

ShortString FObject::ClassName(TClass cls)
{
ShortString* r = *(ShortString**)((char*)cls + vmtClassName);
return *r;
}

我们不妨测试一下。
#include
#include
#include
using namespace std;
... 插入FObject相应的代码...
void main()
{
auto_ptr list(new TList);
FObject* p = (FObject*)list.get();
cout << AnsiString(p->ClassName()).c_str() << endl;
cout << AnsiString(list->ClassName()).c_str() << endl;
}

输出结果在我们意料之内,都是“TList”。
对于函数ClassNameIs,我们就可以轻而易举地完成了。

bool FObject::ClassNameIs(TClass cls, const AnsiString string)
{
return string==ClassName(cls);
}

有朋友可能奇怪,你怎么知道TObject::ClassName是这样的呢?
三种办法:

猜,用经验推测;
看Borland提供的原始码;
看编译以后的汇编码。
在Borland提供的原始码中,我们可以看到TObject::ClassName的实现如下:

class function TObject.ClassName: ShortString;
asm
{ -> EAX VMT }
{ EDX Pointer to result string }
PUSH ESI
PUSH EDI
MOV EDI,EDX
MOV ESI,[EAX].vmtClassName
XOR ECX,ECX
MOV CL,[ESI]
INC ECX
REP MOVSB
POP EDI
POP ESI
end;

熟悉汇编的朋友就可以由此写出相应的C/C++代码来。对于不会的朋友,根据我们的讲解,相信也可以轻而易举地完成吧。
希望您在看这段的时候,不妨先用第1种办法,然后结合2、3看看,一定收获不小。

势如破竹
接下来就太简单了,我们不再举例,把相应的成员函数补充完整即可。您不妨先自己试着写写,探索一下,再与汇编代码和文中的代码作比较,一定乐趣无穷。

TObject::ClassInfo是做什么的?问我啊?我也不知道。VCL的帮助里说,用ClassInfo可以访问包含对象类型、祖先类和所有published属性信息的RTTI表。这个表只是内部使用,TObject提供了其它方法来访问RTTI信息。我们先写出它的实现。



图6 TObject::ClassInfo的运作方式

void * FObject::ClassInfo(TClass cls)
{
return *(void**)((char*)cls + vmtTypeInfo);
}

Borland的说法可信吗?这个函数返回值的类型是void *,明摆着不愿意透露更多的信息。您不妨按上面ClassName的方法测试一下,对于TList,ClassInfo输出的结果居然是0!也就是一个空指针!什么东东?别急,后面我们会掀开这个void *的面纱,现在姑且卖个关子。
VCL框架中只存在单继承,这是由Object Pascal语言的特性决定的。这样,每一个类只有唯一一个父类,函数TObject::ClassParent就能帮您把父类找出来。

TClass FObject::ClassParent(TClass cls)
{
TClass* r = *(TClass**)((char*)cls + vmtParent);
return (r)? (*r) : 0;
}

由此,我们也能很轻松地模拟TObject::InheritsForm的实现。
bool FObject::InheritsFrom(TClass cls, TClass aClass)
{
while(aClass)
{
if(aClass==cls)return true;
cls = ClassParent(cls);
}
return false;
}

要知道一个对象所占的字节数,TObject::InstanceSize就可以达到目的。
long FObject::InstanceSize(TClass cls)
{
return *(long*)((char*)cls + vmtInstanceSize);
}

有朋友可能说,C++不是有sizeof操作符吗?为什么不用呢?在VCL中,sizeof有两个缺陷。首先sizeof是完全静态的,也就是说,如果您写sizeof(...),编译以后,这会被替换为一个常数,没有任何的求值过程,因此不能动态求值;其次,VCL类必须与指针或引用的形式存在。所以对于
TObject *a;
...

sizeof(*a)这样的表达式是错误的。而且即使TObject不是VCL类,使用sizeof(*a)还是相当于sizeof(TObject),没有实际价值。
xzgyb 2002-12-09
  • 打赏
  • 举报
回复
是虫是龙都是空
敲锣打鼓咚咚咚


其实简简单单的一个TObject.Create,背后编译器做了很多东西
myy 2002-12-09
  • 打赏
  • 举报
回复
接:

是虫是龙都是空
▲▲★★一场梦
caimouse 2002-12-05
  • 打赏
  • 举报
回复
不要说那么多了,
帮忙怎么样解决完全用C++写一个TOBJECT出来吧.
并且能用C++BUILDER.
invalid 2002-12-04
  • 打赏
  • 举报
回复
是虫是龙都是空...

下一句谁来接?
szbug 2002-12-04
  • 打赏
  • 举报
回复
呵呵,pazee(耙子)大哥,不介意。。。哈哈,我无所谓。
caimouse 2002-12-04
  • 打赏
  • 举报
回复
是吗?
我很久以前就写了.
耙子 2002-12-04
  • 打赏
  • 举报
回复
你叫“深圳龙”
你这不是明摆着想气死“深圳虫”吗?
caimouse 2002-12-04
  • 打赏
  • 举报
回复
http://www.codediy.com/codebbs/index.asp
没有人来吗

604

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder VCL组件使用和开发
社区管理员
  • VCL组件使用和开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧