(delphi11最新学习资料) Object Pascal 学习笔记---第13章第6节 (嵌套的Finally代码块)

13.6.2 嵌套的Finally代码块

​ Finally代码块可能是确保程序安全最重要、最常用的技术。我不认为这是一个高级话题,但你是否在所有地方都使用了 finally?在边界情况下,例如嵌套操作中,你是否正确使用了finally,还是在一个finally块中合并了多个finalization语句?这是一个远非完美的代码示例:

procedure TForm1.BtnTryFClick(Sender: TObject);
varA1, A2: TAClass;
beginA1 := TAClass.Create;A2 := TAClass.Create;tryA1.Whatever := 'One';A2.Whatever := 'Two';finallyA2.Free;A1.Free;end;
end;

​ 这是相同代码更安全,更正确的版本(再次从SafeCode示例中提取):

procedure TForm1.BtnTryFClick(Sender: TObject);
varA1, A2: TAClass;
beginA1 := TAClass.Create;tryA2 := TAClass.Create;tryA1.Whatever := 'One';A2.Whatever := 'Two';finallyA2.Free;end;finallyA1.Free;end;
end;
13.6.3 动态类型检查

​ 一般来说,类型之间的动态转换操作,尤其是类类型之间的动态转换操作,是另一个陷阱可能的来源。特别是如果您不使用 is 和 as 操作符,而只是进行硬类型转换。事实上,每一次直接类型转换都是潜在的错误源(除非它遵循 is 检查)。

​ 从对象到指针、从类引用到类引用、从对象到接口、从字符串到字符串的类型转换可能非常危险,但在某些特殊情况下很难避免。例如,你可能想在组件的 Tag 属性中保存对象引用,而 Tag 属性是一个整数,因此你不得不进行硬转换。另一种情况是使用老式的 TList(而不是类型安全的泛型列表,将在下一章介绍)将对象保存在指针列表中。

​ 这里有一个相当愚蠢的例子:

procedure TForm1.BtnCastClick(Sender: TObject);
varList: TList;
beginList := TList.Create;tryList.Add(Pointer(Sender));List.Add(Pointer(23422));// 直接类型转换TButton(List[0]).Caption := 'Ouch';TButton(List[1]).Caption := 'Ouch';finallyList.Free;end;
end;

​ 运行这段代码通常会导致访问违规。

注解:这里我说 "一般 "是因为当你随机访问内存时,你永远不知道实际效果如何。有时,程序只是覆盖了内存,并不会立即导致错误,但事后你就很难弄清楚为什么其他数据会被破坏。

​ 你应该尽可能避免类似情况的发生,但如果你碰巧别无选择,又该如何修复这段代码呢?最自然的方法是使用as安全类型转换(as safe cast)或is类型检查(is type check),就像下面的代码片段一样:

// "as"类型转换
(TObject(List[0]) as TButton).Caption := 'Ouch';
(TObject(List[1]) as TButton).Caption := 'Ouch';
// "is"类型转换
if TObject(List[0]) is TButton thenTButton(List[0]).Caption := 'Ouch';
if TObject(List[1]) is TButton thenTButton(List[1]).Caption := 'Ouch';

​ 然而,这并不是解决问题的办法,你会继续遇到访问违规。问题在于,is 和 as 最终都会调用 TObject.InheritsFrom,而这是一种很难在数字上执行的操作!

​ 解决办法是什么?真正的解决办法是首先避免出现类似情况(老实说,这类代码没有什么意义),例如使用 TObjectList 或其他安全技术(再次参阅下一章的通用容器类)。如果你真的喜欢低级黑客并喜欢玩指针,你可以试着找出给定的 "数字值 "是否真的是一个对象的引用。不过,这并不是一个简单的操作。这其中还有有趣的一面,我将以此为借口在下面的演示中解释对象和类引用的内部结构。

13.6.4 这个指针是对象引用吗

​ 本节解释了对象和类引用的内部结构,远远超出了本书大部分内容的讨论范围。尽管如此,它仍能为更专业的读者提供一些有趣的见解,因此我决定保留这部分内容,这些内容来自我以前写过一篇关于内存管理的高级论文。还要注意的是,下面内存检查方面的具体实现只是适用于 Windows系统。

​ 有时,你会有一些指针(指针只是一个数值,指的是某些数据的物理内存位置)。这些指针实际上可能是对对象的引用,你通常知道它们是什么时候的引用,并将它们作为引用使用。但是,每当你进行一次低层次的转换时,你就真的快要把整个程序搞砸了。有一些技术可以让这种指针管理更安全一些,即使不能保证百分之百安全。

​ 在使用指针之前,你可能需要考虑的问题是,它是否是一个合法的指针。Assigned 函数只能检查指针是否为 nil,在这种情况下并没有帮助。不过,Object Pascal RTL(Windows 平台上的 System 单元)中鲜为人知的 FindHInstance 函数会返回堆块的基地址,其中包括作为参数传递的对象,如果指针指向的是无效页,则返回 0(防止出现频率很低但极难跟踪的内存页错误)。如果随便取一个数字,它很可能不是指向一个有效的内存页。

​ 这是一个很好的起点,但我们可以做得更好,因为如果值是字符串引用或任何其他有效指针,而不是对象引用,这样做也无济于事。现在,如何知道指针是否真的是对象引用呢?我想出了以下经验测试方法。对象的前 4 个字节是指向其类的指针。如果考虑类引用的内部数据结构,那么它的 vmtSelfPtr 位置就是指向自身的指针。如图 13.7 所示。

图 13.7:对象和类引用内部结构的大致示意图

​ 换句话说,从类引用指针(这是一个负偏移量,在内存中较低的位置)解引用内存位置 vmtSelfPtr 字节上的值,就可以再次获得相同的类引用指针。此外,在类引用的内部数据结构中,可以读取实例大小信息(位于 vmtInstanceSize 位置),看看其中是否有合理的数字。以下是实际代码:

function IsPointerToObject(Address: Pointer): Boolean;
varClassPointer, VmtPointer: PByte;InstSize: Integer;
beginResult := False;if FindHInstance(Address) > 0 thenbeginVmtPointer := PByte(Address^);ClassPointer := VmtPointer + vmtSelfPtr;if Assigned(VmtPointer) and (FindHInstance(VmtPointer) > 0) thenbeginInstSize := (PInteger(VmtPointer + VmtInstanceSize))^;// 检查Self指针和“合理”的实例大小if Pointer(Pointer(ClassPointer)^ = Pointer(VmtPointer)) and(InstSize > 0) and (InstSize < 10000) thenResult := True;end;end;
end;

注解 此函数返回正确值的概率非常高,但并非百分之百。不幸的是,内存中的随机数据可能会通过测试。

​ 有了这个函数,在前面的 SafeCode 示例中,我们可以在进行安全转换之前添加一个指针到对象的检查:

if IsPointerToObject(List[0]) then(TObject(List[0]) as TButton).Caption := 'Ouch';
if IsPointerToObject(List[1]) then(TObject(List[1]) as TButton).Caption := 'Ouch';

​ 同样的想法也可以直接应用于类引用,以实现它们之间的安全转换。同样,最好首先通过编写更安全、更简洁的代码来避免类似问题,但万一无法避免,IsPointerToObject 函数可能会派上用场。无论如何,本节应该已经解释了这些系统数据结构的一些内部构造。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/19938.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA启动jsp项目

1、背景 有个老项目的前端需要修改&#xff0c;整来源码之后发现是比较古老的jsp项目&#xff0c;需要在idea中启动下试试 2、代码配置流程 常规的配置流程网上都有 2.1 首先找到Project Structure 2.2 配置web.xml 注意下方的 web resource directory, web.xml中的写的相对…

Markdown 使用技巧之利用 Mermaid 进行绘制流程图

文章目录 前言一、基础语法1.1 声明图像类型1.2 声明排列方向1.3 声明节点1.4 声明节点形状1.5 声明节点间的连接1.5.1 基本连接线1.5.2 调整链接的长度1.5.3 调整链接的样式二、流程图-进阶使用2.1 自定义节点样式2.2 自定义形状大小2.3 自定义链接样式2.4 视图分组三、使用场…

【代码随想录算法训练营第37期 第二十四天 | LeetCode77. 组合】

代码随想录算法训练营第37期 第二十四天 | LeetCode77. 组合 一、77. 组合 解题代码C&#xff1a; class Solution { private:vector<vector<int>> result;vector<int> path;void backtracking(int n, int k, int startIndex){if(path.size() k){result.p…

校园安保巡逻机器人

2023年8月5日&#xff0c;陕西西安一高校实验室起火冒烟&#xff0c;导致学校化学实验室发生火灾。2022年8月3日&#xff0c;一名歹徒持械闯入江西吉安安福县城的一家私立幼儿园&#xff0c;对着无辜的幼儿行凶伤人&#xff0c;造成3死6伤。 像这样的事故有不断地发生&#xf…

Flutter 中的 DividerTheme 小部件:全面指南

Flutter 中的 DividerTheme 小部件&#xff1a;全面指南 Flutter 是一个功能强大的 UI 框架&#xff0c;由 Google 开发&#xff0c;允许开发者使用 Dart 语言来构建跨平台的移动、Web 和桌面应用。在 Flutter 的丰富组件库中&#xff0c;DividerTheme 是一个专门用于定义应用…

代码随想录leetcode200题之贪心算法

目录 1 介绍2 训练3 参考 1 介绍 本博客用来记录代码随想录leetcode200题之贪心算法相关题目。 2 训练 题目1&#xff1a;455. 分发饼干 C代码如下&#xff0c; class Solution { public:int findContentChildren(vector<int>& g, vector<int>& s) {s…

161.二叉树:在每个树中找最大值(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* Tre…

Kong网关代理MQTT的两种方法

我的前一篇文章&#xff0c;是关于Kong网关实现TCP流代理的方法。使用的目前最新版本的Kong网关软件&#xff0c;新版自带 Kong Manager UI界面。但2.*版本没有提供 UI界面&#xff0c;如果实现TCP流代理。有两种方法&#xff0c;一种是使用第三方 konga 的UI界面来操作&#x…

C语言王国——杨氏矩阵

目录 1. 引言 2. 了解杨氏矩阵 3. 思路分析 4. 代码 5. 总结 1. 引言 最近在做二维数组的训练的时候发现了一个很有意思的题&#xff1a; 一看这不是杨氏矩阵嘛&#xff0c;接下来就由姜糖我带大家了解一下这个著名的矩阵。 2. 了解杨氏矩阵 通过查阅百度得知&#xff1a; …

python数据分析——datetime数据类型1

参考资料&#xff1a;活用pandas库 1、python的datetime对象 # 导入datetime对象 from datetime import datetime# 获取当前日期和时间 nowdatetime.now() print(now)# 手动创建datetime t1datetime.now() t2datetime(1970,1,1) # 对datetime做数学运算 difft1-t2 print(diff…

儿童节快乐!探索图形化编程桌面的“童年”成长之路

在这个充满童真与快乐的儿童节&#xff0c;我要向在CSDN平台上努力拼搏的每一位朋友&#xff0c;送上我最热切、最深情的祝福&#xff01;愿你们心中那份孩童般的纯真与对世界无尽的好奇永不褪色&#xff0c;愿你们的人生道路如同这个美好的节日&#xff0c;流光溢彩、欢乐永恒…

lynis安全漏洞扫描工具

Lynis是一款Unix系统的安全审计以及加固工具&#xff0c;能够进行深层次的安全扫描&#xff0c;其目的是检测潜在的时间并对未来的系统加固提供建议。这款软件会扫描一般系统信息&#xff0c;脆弱软件包以及潜在的错误配置。 安装 方式1 git下载使用git clone https://github…

华为校招机试 - 字符串解析(20240515)

题目描述 已知存在某种字符串解析语法,其中的语法元素如下 N:用于匹配单个数字(0-9)A:用于匹配单个字母(a-z,A-Z)n():用于表示一个分组,分组中至少有一个N语法元素或者A语法元素,n为个数值,表示匹配 n 次,1 ≤ n ≤ 200 输入给定的解析语法和目标字符串,要求从中…

docker compose完成简单项目部署

1. 项目环境 centos7 docker mysql redis ruoyi项目 ruoyi项目链接&#xff1a;https://gitee.com/y_project/RuoYi-Vue.git 2. 进行项目前后端代码打包 后端打包&#xff1a; 修改mysql连接的相关配置文件 RuoYi-Vue/ruoyi-admin/src/main/resources/application-dru…

浙江零排参加全国水科技大会暨技术装备成果展览会(成都)并作主论坛演讲

2024年5月13日-15日中华环保联合会、福州大学、上海大学等联合举办的2024年全国水科技大会暨技术装备成果展览会在成都顺利举办。浙江零排城乡规划发展有限公司司受邀参加&#xff0c;首日有幸听取徐祖信院士、任洪强院士、汪华林院士等嘉宾的主旨报告。主旨报告后&#xff0c;…

Java过滤特殊空格nbsp;

现象&#xff1a; 用Java处理excel文件中的以下字符串时&#xff0c;想去除此空格&#xff0c;却发现用String.trim()没有直到预期效果&#xff1a; 原因&#xff1a; 在网上找了下&#xff0c;应该是这其实是html中经常使用的一种特殊空格字符&nbsp&#xff1b; 处理&a…

第八十九周周报

学习目标&#xff1a; 论文 学习时间&#xff1a; 2024.05.25-2024.05.31 学习产出&#xff1a; 一、论文 SAN: INDUCING METRIZABILITY OF GAN WITH DISCRIMINATIVE NORMALIZED LINEAR LAYER 将GAN与切片最优输运联系起来&#xff0c;提出满足方向最优性、可分离性和单射…

Redis基础操作命令

Redis基础操作命令非常丰富&#xff0c;涵盖了多个方面&#xff0c;包括全局操作、字符串操作、哈希操作、列表操作、集合操作以及有序集合操作等。以下是Redis基础操作命令的归纳和详细解释&#xff1a; 1. 全局操作 ping&#xff1a;检查Redis服务是否运行正常&#xff0c;…

DBeaver连接Elasticsearch

一、下载DBeaver 二、连接&#xff1a; 1、一定要选择开源的 Open Distro Elasticsearch 2、填写地址&#xff1a; 3、选择“URL”&#xff0c;将https改为http 否则会报SSL错误 4、测试连接

JavaScript笔记三-JavaScript常用对象

1、数字对象&#xff08;Number&#xff09; 1.1 、属性 属性描述Number.MAX_VALUEJavaScript 中所能表示的最大值Number.MIN_VALUEJavaScript 中所能表示的最小值Number.NaN非数字Number.NEGATIVE_INFINITY负无穷&#xff0c;在溢出时返回Number.POSITIVE_INFINITY正无穷&am…