对Object Pascal编译器给类对象分配堆内存细节的一种大胆猜测(下)

到了这里,你也许会说,说了半天,都是猜测,或许,OP编译器根本就不会调用那个TObject.NewInstance方法呢!

问得好,再做实验!

还是以上面的那个Tbase类为例,重载TObject.NewInstance方法,如下:

TBase = class(TObject)

    x : Integer;

    y : Double;

    class function NewInstance: TObject; override;

    procedure FreeInstance; override;

    constructor Create;

  end;

 

{实现}

constructor TBase.Create;

begin

  self.x := 2;

  self.y := 3.14;

end;

 

procedure TBase.FreeInstance;

begin

  inherited;

  ShowMessage(Format('Call %s.FreeInstance!!!',[self.ClassName]));

end;

 

class function TBase.NewInstance: TObject;

begin

  ShowMessage(Format('call %s.NewInstance',[self.ClassName]));

  result := inherited NewInstance;

end;

 

之后进行简单的声明对象:

var

b : Tbase;

begin

b := Tbase.Create;     ß在这里设断点!

b.Free;

end;

通过对代码进行跟踪果然在一进入Create就马上调用NewInstance方法。

[说明:一定要重载它才能跟踪到它,在断点处,观察CPU,从反汇编后的代码中可以发现,是先调用一个_ClassCreate,然后才调用NewInstance]

用同样的方法可以分析出b.Free会最终调用到FreeInstance;来释放对象。

 

我想基本上大的问题已经说请了,Object Pascal为了实现分配堆内存,在你调用构造器的时候:

b := Tbase.Create;

在构造方法内你的代码前,安插了代码调用NewInstance方法,析构时,则在析构函数中你的代码后,调用FreeInstance函数。

 

那么,现在再来看这种情况:派生

TBase = class(TObject)

    x : Integer;

    y : Double;

    class function NewInstance: TObject; override;

    procedure FreeInstance; override;

    constructor Create;

  end;

 

  TSub = class (TBase)

    m : Integer;

    n : Double;

    constructor Create;

 end;

 

{实现}

constructor TBase.Create;

begin

  self.x := 2;

  self.y := 3.14;

end;

 

procedure TBase.FreeInstance;

begin

  inherited;

  ShowMessage(Format('Call %s.FreeInstance!!!',[self.ClassName]));

 

end;

 

class function TBase.NewInstance: TObject;

begin

  ShowMessage(Format('call %s.NewInstance',[self.ClassName]));

  result := inherited NewInstance;

 

end;

 

{ TSub }

 

constructor TSub.Create;

begin

  inherited Create;         ß注意这里!

  self.m := 4;

  self.n := 12.32;

end;

我们已经知道,

var

s : Tsub;

 

s := Tsub.Create;

时,在进入Tsub.Create内部马上得到了它想要的内存[这里是32字节],那么当:

inherited Create;时,在Tbase.Create内部,还有内存分配的动作吗?我们可以通过三点证明:这里,Tbase.Create只是完成程序员给出的初始化代码,没有进行内存分配的动作。

第一点,ReturnValue := inherited Create;所得到的返回地址和调用Tsub.Create所得到的返回地址相同。

第二点,如果在Tbase.Create内部又分配新的内存,那么

self.x := 2;

self.y := 3.14;

只是针对新的内存操作,而原来的S对象中从TBASE中继承来的XY不会变,还是0,但我们发现,S中的XY已经改变,所以也可以证明Tbase.Create没有分配新的内存,只是对原有内存中的XY进行设置。

第三点,跟踪。这是最简单,最一目了然的方法,看看inherited Create;到底有没有调用NewInstance,实验证明,跟本没有调用。

 

但是,如果把Tsub.Create中的inherited Create;改为Tbase.Create;情况则大不同了,用上面三种方式发现,它又分配了新的堆内存,这样不但没有达到程序员初始化数据的目的,反而造成了内存泄漏,而这样的BUG是很难找到的。

也就是说,编译器发现如果是通过类来调用构造函数,就会当成是新的类对象进行构造、分配堆内存,如果是在构造器内部inherited Create;只是按常规的处理 类方法 的方式进行处理。我想,对于Anders Hejlsberg[DELPHI设计者],想在编译器中实现这样的功能并非一件难事[实际上,我们通过查看汇编代码也能分析出个中原由,有兴趣者请注意其中的TEST d1,d1指令和其下的跳转指令]

 

 

PS:刚才被网友告知有本书叫《delphi的原子世界》,我很想得到它,如果您手上有它的E-BOOK版,希望您能发给我: coder@vip.sina.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
# Uchat——基于python的安全即时通讯系统 ## 目的 设计完成简易的安全即时通讯系统,实现类似于QQ的聊天软件; ## 需求分析 ### 功能需求 1. 聊天客户端 1. 注册:用户与集中服务器通信完成注册,包括用户名、密码、邮箱、性别、年龄、数字证书等信息传输,其中数字证书包含公钥、用户名、邮箱等信息。私钥单独保存在客户端一个文件夹下不进行传输;能显示用户名、邮箱不符合格式规范或者重复,空输入等错误信息。 2. 认证登录:客户端与集中服务器通信完成用户名、口令认证登录;能显示用户名、密码错误导致的登录错误信息。还有已登录账号再次登录时的多重登录检验,并将之前登陆的账号顶下去。 3. 好友管理:用户可通过服务器进行搜索、添加、删除好友。 4. 即时通信:用户通过客户端实现与好友的聊天,包括文字、图片传输。文字可实现字体颜色和大小的改变。 5. 聊天记录:客户端能够保存聊天记录并且可以查看聊天记录。 6. 消息加解密:采用D-H体制协商加密秘钥,用对称密码AES算法进行加解密。 7. 消息摘要:使用MD5算法实现消息摘要认证功能,确保发送消息的完整性。 8. 用户未读消息提醒:红点标注未读消息数目,并按最后发送消息时间排列好友列表。 9. 用户离线后消息处理:用户上线后及时接收到离线时好友发送的消息。 功能结构图 ![](https://leeyuxun-1258157351.cos.ap-beijing.myqcloud.com/img/20200714124535.png) 2. 集中服务器 1. 用户注册:与用户通信完成注册,对用户名和邮箱格式、是否重复,输入不规范等做必要的检验,接收客户端的数字证书,发送服务端数字证书。 2. 登录验证:用户登录时,验证用户名和密码是否正确,并向客户端返回登录结果。如信息正确,就将在线好友用户发给该用户,将该用户的状态发给各在线好友用户,同时在服务器端显示出来。 3. 用户公钥,证书提供:用户向好友发送消息时,与服务器建立安全连接获取好友的证书信息,服务器控制client.socket像好友用户发送信息,实现用户之间的通信。 4. 用户在线状态维护:当用户在线时,记录保存用户的在线状态、IP地址、端口号。 5. 用户消息列表实时发放:由监听函数将操作实时加入到执行函数列表中递归执行。向用户发送其好友列表的在线离线情况,包括好友用户名、IP地址、端口号。并按照最后发消息的时间对好友消息列表进行排序。 功能结构图 ![](https://leeyuxun-1258157351.cos.ap-beijing.myqcloud.com/img/20200714124653.png) 3. 高级功能 1. 离线用户消息通知:暂时存储离线用户的消息,用户上线后,显示未读的消息并用红点标注; 2. 好友在线离线功能实时更新; 3. 限制账号只能一处登录:一个账号只能在一处登录,在别处登录时会把原先的登录踢下线; 4. 支持群聊功能:可以创建群聊,并根据群号加入群聊; 5. 聊天时字体大小颜色可更改; 6. 支持聊天各类图像文件的缓存。 -------- 该资源内项目源码是个人的毕设,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! <项目介绍> 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 --------
## 密码管理器 食用指北 *** ### 用途 这是一个 _Python3_ 基于pyside6开发的一个密码管理器,用于加密存储我们在日常中的各种密码。解决使用复杂密码但是又怕记不住,简单密码有不安全的老大难问题。 **功能** * 多用户使用 * 数据本地化管理 * 本地存储使用多种加密方式 *** ### 开发初衷 很多小伙伴应该都有被盗号的经历吧。首先有些黑客不得不说确实非常厉害,轻轻松松就能把你的账号盗走。 但是也存在自己慢慢实在太简单了,这种密码叫做弱密码,就比如说123456这种密码。 怎么说呢,这种密码确实很容易记,不知道您清不清楚这种密码在很多爆破字典中都是第一个,也就意味着,别人只要一式就知道你的密码了。 `也就被盗了。` 怎么说呢,这种情况是真的存在的,虽然这种电脑软件可能没那么实用,毕竟更多人用的都是手机,但是吧! 俺不会开发APP,我不是程序员,俺只会Python。 当然有的小伙伴可能会说开发网站,小程序什么的,这个的话还真不行,虽然我可以做,但是就算做出来,我自己都不会存。因为这些都会需要在服务器中,这种要是这个服务器被黑客攻击呢!不可控因素太多了! 我们生活在这透明的世界上,虽然没有隐私可言,但是还是保护一下自己,不要再用那种弱密码了。 实现功能 *** ### 准备工作 您需要准备以下东西 * _Python3.9_ 及以上版本(或自行改写旧版不兼容的语法) * _peewee_ 3.15.3 * _pycryptodome_ 3.15.0 或其等价替代 * _PySide6_ 6.3.2 部分版本需要自行修改窗口移动代码,会存在不兼容 *** ### 如何使用 *** #### 源码运行 -------- 该资源内项目源码是个人的毕设,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! <项目介绍> 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 --------
总体概括 注册流程 首先进行输入用户名(邮箱)、密码以及验证码,输入完之后点击注册按钮。如果输入的不正确,提示错误信息。 如果一切信息填写正确无误,调用STMP模块发送激活邮件,用户必须要点击接收到邮箱链接,进行邮件激活后才方可登陆。 即使注册成功,没有激活的用户也不能登陆,用户以get的方式直接重定向到注册页面。 注册登录: 用户能在系统中进行登陆注册和忘记密码进行找回的功能。 个人中心:修改头像,修改密码,修改邮箱,可以看到我的信息。 日志记录: 记录后台人员的操作,方便发现BUG和查看各项调用进行时间。 导航栏:学生信息中有基本信息、年级及成绩信息的模块,能够排序筛选等功能。 多选操作: 可以选择多条记录进行删除操作,还可以在课程列表页可以对不同课程进行排序。 数据页码: 可以设置各项数据在每一页中显示的数量多少,进行翻页功能。 模块列表页: 能够有过滤器功能,在范围内进行查看数据。还能将数据导出为csv,xml,json等数据格式。 -------- 该资源内项目源码是个人的毕设,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! <项目介绍> 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 --------
要下载Pascal编译器源码,可以按照以下步骤进行: 1. 首先,确定你想要下载的Pascal编译器的具体版本和来源。有许多不同的Pascal编译器可以选择,如Free Pascal、Turbo Pascal等。根据你的需求选择适合的编译器版本。 2. 在互联网上搜索选定编译器的官方网站。通常,官方网站会提供该编译器的源码下载选项。例如,Free Pascal的官方网站是www.freepascal.org,Turbo Pascal的官方网站是www.turbopascal.org。 3. 在选定的官方网站上找到源码下载的页面或部分。通常,官方网站的首页会提供导航或搜索功能来帮助你找到源码下载选项。 4. 点击或导航到源码下载页面。在该页面上,你可能需要选择所需的编译器版本和操作系统平台(如Windows、Linux等)。 5. 确认选择后,官方网站会提供源码下载的链接。点击该链接开始下载。根据源码的大小和你的网络连接速度,下载过程可能需要一些时间。 6. 下载完成后,你将获得一个压缩文件(通常是.tar.gz或.zip格式),其中包含Pascal编译器的源码文件。解压缩该文件并保存在你选择的目标文件夹中。 7. 现在,你可以打开你所选的源代码编辑器(如Visual Studio Code、Sublime Text等),导入下载的源码文件,并开始查看和修改编译器的源代码。 请记住,下载和使用Pascal编译器的源码需要遵循适当的授权和使用规则。确保你理解和遵守相关的许可证要求,并在使用源码前仔细阅读和遵守官方网站提供的文档和准则。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mahongxi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值