{一}对象模型小札记
读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显示21,Edit2显示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中我们用TObject、TComponent等等标识符表示类,它们在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个字节
分享到:
相关推荐
Simulink代码生成学习札记, 对于初学者来说有助于对simulink代码生成有个大体的了解
1. 字体大小:提供五档字体大小,默认为“大”,会改变列表和内容的字体大小 2. 排序方式:札记列表中札记的排序方式,可选择按时间先后或者按标题排序 3. 保存
使用selenium做自动化测试,是一个不错的选择,小结一下,与人分享
使用selenium做自动化测试,是一个不错的选择,小结一下,与人分享
原神多功能机器人,查询游戏信息、图鉴攻略、树脂提醒等等,以及各种各样的好玩的功能,不仅仅是原神。 目前暂只支持onebot协议,正在开发多聊天平台的船新版本。 <项目介绍> 该资源内项目源码是个人的毕设,代码都...
思考,记录。 技术总结 从零开始开发一款H5小游戏系列文章 细说Unicode 有问必答 开源项目&工具 :Anyproxy客户端,抓包和代理两不误 :Api Mock命令行工具,简单易用 :利用gulp和webpack开发... Node.js札记 读书
《C++ Primer》学习札记 C++学习札记(十)关联容器 C++学习札记(十二)动态内存 C++学习札记(十三)拷贝控制 C++进阶及面试必备 命名空间 四种强制类型转换 操作系统面试送分题 什么是程序?什么是进程?它们...
札记代码401-使用Python进行高级软件开发第一课阅读痛苦与痛苦成长的痛苦不同于痛苦一种战斗方式-密切关注奖金提出问题,寻求答案并找到解决办法大O符号入门指南O(1):一种算法,无论输入数据集的大小如何,它将...
札记 这是一个资料库,用于记录我在本课程中阅读的所有信息。 所有已经学到的东西,或者无论如何都应该学到的东西。 Markus Spiske在Unsplash上拍摄的照片 我在课程中开发的报价 “为祖母建造”-保持代码尽可能...
札记 技术要点: RPC: 登录及权限操作redis + session处理 搜索文章基于elasticsearch (内存不够了,暂停使用☻) 评论和关注等消息基于activemq: 数据结果处理: 开发: master采用的是rpc的微服务架构。 正常...
民国二十五年八月商务印书馆出版国学小 书之一重定价三角五分昨偶以事过商务印书馆,见新出版诸书中有是书,随取而观之,首觉其自序,於不苟篇:不诚则不独,不独则不形,俞荫甫释独为无他事,天论篇:君子敬其在己...
我Ro'ya Alakayleh,这是我的自我介绍小:我23岁,我已经得到了从AABU在约旦MLS光棍dedree。 我是一个乐观的人,只要有机会,她就喜欢学习新事物。 在github 上关注我 札记 在这里,我可以跟踪您在整个课程中的...
谷歌师兄的leetcode刷题笔记札记 该文件将用于读取作业。 这个网站将包括我在不同级别阅读作业的笔记。 代码 102 - 软件开发简介 Code 201 - 软件开发基础 代码 301 - 中级软件开发 代码 401 - 高级软件开发 我在...
汉化札记: =================================================== P4MagicSpeed RC2.3修正版,从新用VBExplorer汉化一遍; 主要改正了 系统托盘 的提示问题,和字体(由于第一次汉化VB程序字体并没有修改)! 本来想...
我读 iRead是使用Swift编写的iOS版EPUB阅读器。 特征 Epub 2和Epub 3支持 自定义字体 自定义文字大小 主题/支持4种模式 阅读进度/书页数 解析epub封面图片 ... 札记 阅读目标 家 探索 快照 待续
导航分布式协议Paxos ( ) 筏( ) 八卦( ) RPCRedis 缓存岩石...volume-service desgin 经验分享最小IO 概述思考Tera(C++ HBase) 概述师傅介绍平板节点介绍程序介绍想想札记虚拟存储系统数据密集型应用系统设计