`
javasogo
  • 浏览: 1845075 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论
阅读更多

{一}对象模型小札记

Delphi原子世界中一段的札记:

先看一段话,再根据这段文章用一例子来测试:

对象就是一个带柄的南瓜。南瓜柄就是对象的指针,南瓜就是对象的数据体。确切地说,DELPHI中的对象是一个指针,这个指针指向该对象在内存中所占据的一块空间。我们将对象指针指向的内存空间称为对象空间。对象空间的头4个字节是指向该对象直属类的虚方法地址表(VMT – Vritual Method Table)。接下来的空间就是存储对象本身成员数据的空间,并按从该对象最原始祖先类的数据成员到该对象具体类的数据成员的总顺序,和每一级类中定义数据成员的排列顺序存储。

我们声明一个类:

TMyclass=class

mem1:integer;

mem2:string;

mem3:integer;

end;

再声明一个类变量

Myclass:TMyclass;

然后写一段代码:

myclass:=Tmyclass.Create;

myclass.mem1:=21;

myclass.mem2:='i find you by this way!';

Edit1.Text:=InttoStr(PInteger(Integer(myclass)+4)^);

Edit2.Text:=PString(Integer(myclass)+8)^;

myclass.Free;

运行结果,Edit1显示21Edit2显示i find you by this way!

根据上面的说法,解释如下:myclass实际上是一个指向对象内存空间的指针,所以Integer(myclass)就是对象内存空间的首地址,首地址的4个字节保存对象所属类的VMT,而过了这4个字节的后面4个字节,保存对象的成员Mem1,再过4个字节,保存对象的成员Mem2。那么,Integer(myclass)+4就是成员Mem1的地址啦,而另一个成员Mem2当然是保存在地址为Integer(myclass)+8的内存当中啦。我们将地址为Integer(myclass)+4强制变成一个指针,再得到指针所指向的内存块的值,也即是如下形式:PInteger(Integer(myclass)+4)^当然就是第一个成员的值啦。而同样道理:PString(Integer(myclass)+8)^当然是第二个成员的值啦。

而对象内存空间的首4个字节是类VMT的地址,那么我们再看另一段代码,以得到该对象所属类的一些信息, 在这之前,要求对VMT的结构有一些了解:

Var ClassHead:Integer;

begin

myclass:=Tmyclass.Create;

ClassHead:=PInteger(myclass)^+vmtClassName;

Edit1.Text:= PShortString(Pointer(ClassHead)^)^;

Edit2.Text:=InttoStr(PInteger(Integer(myclass)+4)^);

myclass.Free;

end;

PInteger(myclass)^取得对象首4个字节的值,也即是VMT的地址,而这一句ClassHead:=PInteger(myclass)^+vmtClassName;则是VMT首地址向负方向移负44个字节的地址,该地址的内存中存放了一个指向类名的指针。所以我们如果写了这一句Pointer(ClassHead)^,则是取得了该地址的内存块的值了(也即是取得指向类名短字符串的指针),该值实际上也是一个地址,指向类名短字符串的地址,那么把它强制转一个ShortString的指针,再取指针的值,当然就取得了类名了。

上面是有关对象的内存结构,下面我们来说类的内存结构了,其实上面已经很清楚了,类其实应该是一个VMT的首地址,注意和对象的区别,对象是指向对象内存空间,再由内存空间的首4个字节的值指向VMT的地址。也就是说一个类的多个对象都共享一张VMT

再看下面的一段话:

每一个类都有对应的一张VMT,类的VMT保存从该类的原始祖先类派生到该类的所有类的虚方法的过程地址。在VMT的负方向偏移有76个字节的数据信息,它们是类的基本数据结构。而VMT是存储我们自己为类定义的虚方法地址的地方,它只是类数据结的构扩展部分。VMT前的76个字节的数据结构是DELPHI内定的,与编译器相关的,并且在将来的DELPHI版本中有可能被改变。

DELPHI中我们用TObjectTComponent等等标识符表示类,它们在DELPHI的内部实现为各自的VMT数据。而用class of保留字定义的类的类型,实际就是指向相关VMT数据的指针。

当一个对象产生时,系统会为该对象分配一块内存空间,并将该对象与相关的类联系起来。于是,在为对象分配的数据空间中的头4个字节,就成为指向类VMT数据的指针。

我们再来看一段代码:

Edit1.Text:=InttoStr(PInteger(Integer(Tmyclass)+vmtInstanceSize)^);

Edit2.Text:=PShortString(Pointer(Integer(TMyclass)+vmtClassName)^)^;

Integer(Tmyclass)取得了VMT的首地址的,再向负方向移动vmtInstanceSize-40个字节,即到了保存对象尺寸的内存块的地址,将该地址转为一个指针,再得到指针指向内存块的值,即是类的对象的尺寸了:

PInteger(Integer(Tmyclass)+vmtInstanceSize)^

而另一个也不必过多解释了。

文中有一句话:而用class of保留字定义的类的类型,实际就是指向相关VMT数据的指针。如果它是指向VMT的指针,那么应该也可以通过对他操作而得到VMT的一些数据。先声明一个类引用:TMYCC=class of TMyClass;再定义一个该引用的变量:MYCC:TMyCC;

现在作如下测试:

Edit1.Text:=PShortString(Pointer(Integer(MyCC)+vmtClassName)^)^;

却发现违法访问。

后来一想,我们平常用类引用,多用于增加对象创建的灵活性,比如用一个类引用作为方法的参数,则可以将与类引用相对应的类或它的子类传递进方法,然后由方法的类引用参数来创建对应的对象。

那么类引用应该是要被赋值之后才有意义了。则我们写如下的代码看看:

MyCC:=TMyClass;

Edit1.Text:=PShortString(Pointer(Integer(MyCC)+vmtClassName)^)^;

运行结果果然就得到了类的名字:TMyClass

我们再进一步,声明一个TMyClass的子类:

TMySubClass=class(TmyClass);

然后将代码改为如下:

MyCC:=TmySubClass;

Edit1.Text:=PShortString(Pointer(Integer(MyCC)+vmtClassName)^)^;

运行结果是子类的名字:TMySubClass

一切都清楚了,其实说类引用是一个指向VMT的指针并不准确,类才是一个指向VMT的指针,而类引用的变量也是一个指针,开始时并没有指向VMT,所以一开始必须被赋值,一旦被赋予了一个类(也即指向VMT的指针)则该类引用变量就指向了该类的VMT了。

{二}DLL笔记

下面以DLL简单程序的测试,来展示DLL的用法和各种注意点:

一个DLL程序:

library Project2;

uses

SysUtils,Classes;

{$R *.res}

function Min(X, Y: Integer): Integer; stdcall;

begin

if X < Y then Min := X else Min := Y;

end;

function Max(X, Y: Integer): Integer; stdcall;

begin

if X > Y then Max := X else Max := Y;

end;

exports

Min,

Max;

begin

end.

l 一个调用程序:

unit DLLTest;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

const

DLL='project2.DLL';

type

TForm1 = class(TForm)

Button1: TButton;

Edit1: TEdit;

procedure Button1Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

var

Form1: TForm1;

implementation

{$R *.dfm}

function Min(X, Y: Integer): Integer; stdcall; external DLL;

function Max(X,Y:Integer):Integer; stdcall; external DLL;

procedure TForm1.Button1Click(Sender: TObject);

begin

Edit1.Text:=IntToStr(Min(3,5));

end;

end.

输出结果当然是3

l 另一个调用程序:

unit DLLTest;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

const

DLL='project2.DLL';

type

TForm1 = class(TForm)

Button1: TButton;

Edit1: TEdit;

procedure Button1Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

function Min(X, Y: Integer): Integer; stdcall; external DLL;

function Max(X, Y: Integer): Integer; stdcall; external DLL;

var

Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);

begin

Edit1.Text:=IntToStr(Min(3,5));

end;

end.

输出结果也是3

结论:无论在Interface还是implementation部分声明,都可以,但区别当然也是很明显的,声明在Interface的可以被其他单元调用,而声明在implementation的,只能在自己的单元中被调用。要可以被其他单元调用,还有别一种形式,代码如下:

unit DLLTest;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs, StdCtrls;

const

DLL='project2.DLL';

type

TForm1 = class(TForm)

Button1: TButton;

Edit1: TEdit;

procedure Button1Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

function Min(X, Y: Integer): Integer; stdcall;

function Max(X, Y: Integer): Integer; stdcall;

var

Form1: TForm1;

implementation

{$R *.dfm}

function Min; external DLL;

function Max; external DLL;

procedure TForm1.Button1Click(Sender: TObject);

begin

Edit1.Text:=IntToStr(Min(3,5));

end;

end.

l 将其中一个函数的大小写变一下:

function min(X, Y: Integer): Integer; stdcall; external DLL;

程序出现错误,说明对于DLL的函数声明是大小写敏感的。

l 现在将DLL文件的Exports块修改如下:

exports

Min name ‘Max’,

Max name ‘Min’;

再将上面调用程序运行一遍,输出结果是5,说明,如上声明之后,在DLL程序中的Min函数,它输出为Max了,而Max则输输出为Min了。

l 再将Exports块改为如下形式:

exports

Min name index 1,

Max name index 2;

再将调用程序的的声明变为如下:

function Min; external DLL index 2;

//function Max; external DLL;

运行结果为5,则说明当DLL程序声明Index后,外部程序调用时如果也用Index,则调用DLL哪个函数由Index来决定。如果DLL没有在函数输出后面加Index,则外部程序加上Index无效。

l 将调用程序的函数声明变为如下:

function Min; external DLL name 'Max';

function Max; external DLL name 'Min';

则程序运行结果是5,说明如果用了name,则外部函数实际对应的是DLL中和Name后面的字符串相同的函数,大小敏感。

{三}C基本类型与Delphi类型的对应

//整型

INT64 = Int64 有符号64位整数

INT = Integer 有符号32位整数

LONG = Longint 有符号32位整数

WPARAM = Longint 有符号32位整数

LPARAM = Longint 有符号32位整数

LRESULT = Longint 有符号32位整数

HANDLE = Loingint 有符号32位整数

UINT = LongWord 无符号32位整数

DWORD = DWORD 无符号32位整数

SHORT = Smallint 有符号16位整数

WORD = Word 无符号16位整数

BYTE = Byte 无符号8位整数

//浮点型

FLOAT = Single 4个字节

DOUBLE = Double 8个字节

//字符型

CHAR = Char

WCHAR = WideChar;

PWChar = PWideChar;

LPSTR = PAnsiChar;

LPCSTR = PAnsiChar;

LPCTSTR = PAnsiChar;

LPTSTR = PAnsiChar;

LPWSTR = PWideChar;

LPCWSTR = PWideChar;

//布尔型

BOOL = LongBool; 4个字节

bool = Boolean; 1个字节

分享到:
评论

相关推荐

    桫椤札记 1.0.4.rar

    在数字时代,科技的迅速发展使得我们记录生活的方式正在发生着翻天覆地的变化。...在这款电子日记本的帮助下,无论是日常生活的小细节还是关键时刻的重大事件,都将被完整地记录下来,成为珍贵的记忆。

    参考资料-启功论书札记.zip

    《启功论书札记》是一份珍贵的参考资料,它主要收录了著名学者、书法家启功先生关于书法艺术的深刻见解和心得笔记。启功先生是中国现代书法界的重要人物,他的书法理论与实践对中国书法的发展产生了深远影响。这份...

    生物信息学札记_第三版_浙大版_2010年.pdf

    本文所提到的《生物信息学札记》第三版,由樊龙江在浙江大学多个研究所和实验室的背景下编写,对生物信息学的学习和研究提供了重要的资料。 本书的结构涵盖了生物信息学的多个重要方面,包括基础理论、分子数据库、...

    Simulink代码生成学习札记.zip

    这个“Simulink代码生成学习札记”可能包含了关于如何使用Simulink从模型直接生成可执行代码的重要知识,这对于工程师和开发者来说是一个极其有用的资源,特别是对于初学者。 Simulink的主要功能之一就是代码生成,...

    5-学习札记快速整理软件-使用说明书1

    学习札记快速整理软件是一款专为学习者设计的高效笔记管理工具,旨在帮助用户快速整理、记录和检索学习内容。本文将详细介绍该软件的各个功能、运行环境以及使用方法,以便用户更好地利用这款软件提升学习效率。 **...

    护理札记读书笔记.doc

    自小,她就对病患怀有深切的同情和照顾愿望。尽管出生在一个富裕家庭,她却未因此而安逸享受,反而选择了与传统女性角色相悖的道路。她坚持自学医学,不畏家人和社会的反对,八年时间的积淀,让她的专业知识和实践...

    编辑札记的注意事项修改.ppt

    一份编辑札记的注意事项修改版的精品文档,不仅能够帮助编辑人员在出版流程中避免不必要的错误,还能提升整体工作效率与最终成品的质量。这份名为“编辑札记的注意事项修改.ppt”的文档,就是一份专为编辑出版人员量...

    QQ截屏实现札记和bug分析

    ### QQ截屏实现札记与Bug分析 #### 波及层面与问题背景 在实现WaveSoft静态截屏软件的过程中,作者记录下了多个重要的技术细节,旨在帮助其他希望实现类似QQ截屏功能的开发者们。本文章将围绕这些札记进行深入解析...

    数据库的存储结构自学札记.pdf

    数据库存储结构自学札记 数据库存储结构是数据库系统的核心组件之一,决定了数据库的性能和效率。本札记主要涵盖了数据库存储结构的基本概念、逻辑文件中的记录在物理文件中的实现方式、文件组织方式、定长记录格式...

    html 语言 学习札记

    收藏夹小图标(Favicon)的设置是通过`&lt;link&gt;`标签实现的,将`.ico`格式的图标文件放在根目录下,并链接到`&lt;head&gt;`中,以便在用户收藏网站时显示自定义图标。 为了优化搜索引擎的抓取和理解,可以使用以下`&lt;meta&gt;`...

    一年级数学上册教学札记.doc

    但不可忽视的是,仍有一小部分学生在数数时容易粗心大意,导致出错。为了纠正这一问题,教师在课堂上采取了更加积极的引导策略。比如,让学生描述主题图中各种物品的数量,不仅激发了他们的好奇心,还锻炼了他们对...

    C8051F330学习札记

    ### C8051F330学习札记 #### C8051F330主要特性 **C8051F330**是一款由Silicon Labs(芯科科技)生产的高性能混合信号单片机(MCU),特别适用于需要集成多种模拟和数字外设的应用场景。下面将详细介绍其主要特性...

    MINA使用札记(CumulativeProtocolDecoder使用)

    《MINA使用札记——CumulativeProtocolDecoder使用详解》 MINA(Java Multithreaded Network Application Framework)是一个强大的、高性能的Java网络应用框架,它主要用于构建可伸缩的、高性能的服务端应用,如...

    单片机c语言技巧札记.doc

    较小的数据类型可以节省宝贵的内存资源,但可能限制数值的表示范围;较大的数据类型虽然可以处理更大范围的数值,但会占用更多存储空间。此外,根据单片机的具体架构和需求,可能还需要了解和使用其他特定的数据类型...

    JVM学习札记

    ### JVM学习札记 #### 一、JVM运行机制 ##### 1、JVM的启动流程 JVM的启动过程主要包括以下步骤: 1. **加载配置文件**:JVM启动时,首先会根据当前路径寻找配置文件`JVM.CFG`,这个文件包含了JVM的一些基础设置...

    protel学习札记

    【protel学习札记】 Protel是一款经典的电子设计自动化(EDA)软件,广泛用于电路设计、PCB布局布线等领域。以下是对标题和描述中涉及的一些知识点的详细说明: 1. **走线等长匹配** (Q01) 在高速电路设计中,为了...

    LabVIEW 学习札记 - 第三卷 上

    ### LabVIEW 学习札记 - 第三卷 上 #### 虚拟仪器工程应用实例:应力测量 ##### 8.1 绪论 在这一章节中,作者回顾了之前介绍过的LabVIEW基本概念以及虚拟仪器的设计方法,并强调了学习的目的在于实际应用,即服务...

    (一年级语文教案)让学生在快乐中学习──《小小的船》教学札记.pdf

    《小小的船》的教学札记为我们提供了一个生动的案例。教师们通过创设情境教学,将课文内容与学生们的生活经验相结合,为他们构建了一个充满想象的学习环境。例如,通过展示深蓝天空的图片,教师将学生们带入了一个...

    基于Nonebot2的原神机器人,包括但不限于UID面板查询、抽卡记录分析、游戏攻略图鉴、实时便签、原石札记、群聊学习、群管等功能全部资料+详细文档+高分项目.zip

    基于Nonebot2的原神机器人,包括但不限于UID面板查询、抽卡记录分析、游戏攻略图鉴、实时便签、原石札记、群聊学习、群管等功能全部资料+详细文档+高分项目.zip 【备注】 1、该项目是个人高分项目源码,已获导师指导...

    XX品牌整合规划及推广札记.doc

    与市场平均水平相比,像东风、解放这样的知名品牌,其销售下滑幅度较小。而像欧曼这样的企业,由于采取有效的品牌策略,甚至实现了销售增长。这说明,在特殊时期,品牌的力量可以为企业提供市场保护,增强消费者的...

Global site tag (gtag.js) - Google Analytics