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

ops/2025/2/7 14:15:45/
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.ppmy.cn/ops/45909.html

相关文章

【HTML】tabindex

当给 div 标签以 button 角色&#xff1a; <div role"button">这时要指定其 tabindex&#xff0c;因此正确的写法是&#xff1a; <div role"button" tabindex"0">索引值不应当大于0&#xff0c;见a11y-positive-tabindex

DiffIR论文阅读笔记

ICCV2023的一篇用diffusion模型做Image Restoration的论文&#xff0c;一作是清华的教授&#xff0c;还在NIPS2023上一作发表了Hierarchical Integration Diffusion Model for Realistic Image Deblurring&#xff0c;作者里甚至有Luc Van Gool大佬。模型分三个部分&#xff0c…

计算机网络学习

文章目录 第一章信息时代的计算机网络因特网概述电路交换&#xff0c;分组交换&#xff0c;报文交换计算机网络的定义和分类计算机网络的性能指标常见的三种计算机网络体系计算机网络体系结构分层的必要性计算机网络体系结构分层思想举例计算机网络体系结构中的专用术语 第二章…

JSON 和 XML 的基本概念与区别

当我们讨论数据交换格式时&#xff0c;JSON&#xff08;JavaScript对象表示法&#xff09;和 XML&#xff08;可扩展标记语言&#xff09;无疑是最受欢迎的两种选择。这两者各有优点和缺点&#xff0c;根据具体的应用场景&#xff0c;选择合适的格式可以显著提高开发效率和系统…

SQL 语言:对象关系数据模型

文章目录 嵌套关系复杂类型继承引用类型与复杂类型有关的查询函数和过程总结 对象关系数据模型扩展关系数据模型的方式是通过提供一个包括复杂数据类型和面向对象的更丰富的类型系统。 嵌套关系 嵌套关系模型(Nested Relational Model)是关系模型的一个扩展&#xff0c;域可以…

英伟达(NVIDIA)A800性能及应用场景

英伟达&#xff08;NVIDIA&#xff09;A800是一款高性能的人工智能&#xff08;AI&#xff09;加速卡&#xff0c;设计用于满足大规模深度学习、数据分析以及其他高性能计算需求。以下是其主要性能参数及应用概述&#xff1a; 性能参数&#xff1a; 数据传输速率&#xff1a;…

实战分析:如何窃取用户Cookie并保存到远程服务器

实战分析&#xff1a;如何窃取用户Cookie并保存到远程服务器 引言 在网络安全领域&#xff0c;Cookie是Web应用中用于识别用户身份和保存用户状态的重要机制。不幸的是&#xff0c;Cookie也成为了攻击者的目标&#xff0c;他们通过跨站脚本&#xff08;XSS&#xff09;攻击来…

海外短剧APP/H5 系统开发搭建

目前已经有多个客户用我们搭建的海外短剧系统&#xff0c;在使用中已经取得了较高的收益。目前一个客户打算做日本区域的海外短剧项目&#xff0c;需求已经理清楚了&#xff0c;系统正在搭建中