(delphi11最新學習資料) Object Pascal 學習筆記---第5章第3節(自定義托管記錄)

5.3.5 運算符和自定義托管記錄

? 在 Delphi 語言中,有一組特殊的運算符可用于記錄,以定義自定義托管記錄。在此之前,請允許我回顧一下記錄內存初始化的規則,以及普通記錄和托管記錄之間的區別。

? Delphi 中的記錄可以包含任何數據類型的字段。當記錄具有普通(非托管)字段(如數值或其他枚舉值)時,編譯器無需做太多工作。創建和處置記錄只需分配或釋放內存區域即可。(請注意,默認情況下,Delphi 不會對記錄進行零初始化,但會對數組進行零初始化,正如我們稍后將學習的,也會對新對象實例進行零初始化)。

? 如果記錄的字段屬于編譯器管理的類型(如字符串或接口),編譯器需要注入額外的代碼來管理初始化或終止。例如,字符串是有引用計數的,因此當記錄超出作用域時,記錄中的字符串需要減少其引用計數,這可能會導致為字符串去釋放內存。因此,當你在某部分代碼使用托管記錄時,編譯器會自動在代碼周圍添加一個 try-finally 塊,以確保即使出現異常也能清除數據。長期以來,Delphi 語言中的托管記錄一直是這種情況。

? 從 10.4 開始,除了編譯器為托管記錄執行的默認操作外,Delphi 記錄類型還支持自定義初始化(initialization)和終止化(finalization)。無論記錄字段的數據類型如何,您都可以聲明帶有自定義初始化和最終化代碼的記錄,也可以編寫此類自定義初始化和最終化代碼。這些記錄被稱為 “自定義托管記錄”。

? 開發人員可以通過在記錄類型中添加一個或多個特定的新操作符,將記錄轉化為自定義托管記錄:

  • Initialize 運算符在為記錄分配內存后調用,允許您編寫代碼來設置字段的初始值
  • Finalize 運算符在為記錄釋放內存之前調用,允許您執行任何必要的清理
  • Assign 運算符在將記錄數據復制到相同類型的另一條記錄時調用,因此您可以以自定義方式從一條記錄復制信息到另一條記錄

注解:由于托管記錄的清理即使在發生異常時也會執行(編譯器會自動生成try-finally塊),它們通常被用作保護資源分配或執行清理操作的替代方式。我們將在第9章的“使用托管記錄還原光標”部分中看到此用法的示例。

記錄的 InitializeFinalize 運算符

? 我們用以下簡單的代碼片段介紹初始化和終止化:

typeTMyRecord = recordValue: Integer;class operator Initialize(out Dest: TMyRecord);class operator Finalize(var Dest: TMyRecord);end;

? 當然,您需要為這兩個類方法編寫代碼,例如,可以記錄其執行情況或初始化記錄的 Value 字段。在本例(ManagedRecords_101 示例項目的一部分)中,我對 Value 字段進行了初始化,并記下了對內存位置的引用,以便查看執行每個操作的記錄:

class operator TMyRecord.Initialize(out Dest: TMyRecord);
beginDest.Value := 10;Log('Created' + IntToHex(Integer(Pointer(@Dest)))));
end;class operator TMyRecord.Finalize(var Dest: TMyRecord);
beginLog('Destroyed' + IntToHex(Integer(Pointer(@Dest)))));
end;

? 這種構造機制與以前的記錄機制的區別在于自動調用。如果你編寫了類似下面的代碼,你就可以同時調用初始化和終止化代碼,最后由編譯器為你的托管記錄實例生成一個 try-finally 塊:

procedure LocalVarTest;
varMy1: TMyRecord;
beginLog(My1.Value.ToString);
end;

使用上述代碼,您將獲得類似于以下內容的日志(地址將有所不同):

Created 0019F2A8
10
Destroyed 0019F2A8

另一個場景是使用內聯變量,例如:

beginvar T: TMyRecord;Log(T.Value.ToString);

這將在日志中產生相同的序列。

賦值運算符

? 一般來說,賦值操作符(:=)會直接復制記錄字段的所有數據。編譯器也會正確處理具有托管類型(如字符串)的記錄。

? 如果有自定義數據字段和自定義初始化,則您可能需要更改默認行為。因此,您也可以為自定義托管記錄定義賦值操作符。新操作符使用 := 語法調用,但定義為 Assign

class operator Assign(var Dest: TMyRecord; const [ref] Src: TMyRecord);

運算符定義必須遵循非常精確的規則,包括第一個參數必須是引用傳遞(var)的參數,將第二個參數是引用傳遞的const參數。如果未這樣做,編譯器將報出以下錯誤消息:

[dcc32 Error] E2617 First parameter of Assign operator must be a var
parameter of the container type
[dcc32 Hint] H2618 Second parameter of Assign operator must be a
const[Ref] or var parameter of the container type

這是調用 Assign 運算符的一個示例:

varMy1, My2: TMyRecord;
beginMy1.Value := 22;My2 := My1;

這將產生以下日志(我還為記錄添加了一個序列號):

Created 5 0019F2A0
Created 6 0019F298
5 copied to 6
Destroyed 6 0019F298
Destroyed 5 0019F2A0

請注意,銷毀的順序與構建的順序相反,最后創建的記錄是第一個銷毀的。

將托管記錄作為參數傳遞

? 托管記錄在作為參數傳遞或由函數返回時,其工作方式也與普通記錄不同。下面的幾個例程展示了各種情況:

procedure ParByValue(Rec: TMyRecord);
procedure ParByConstValue(const Rec: TMyRecord);
procedure ParByRef(var Rec: TMyRecord);
procedure ParByConstRef(const [ref] Rec: TMyRecord);
function ParReturned: TMyRecord;

現在,無需逐一檢查每個日志(您可以運行ManagedRecords_101演示來查看它們),這是信息摘要:

  • ParByValue 創建一個新記錄并調用賦值運算符(如果可用)來復制數據,在超出范圍時銷毀臨時副本
  • ParByConstValue 不進行復制,也不調用任何內容
  • ParByRef 不進行復制,也不調用任何內容
  • ParByConstRef 不進行復制,也不調用任何內容
  • ParReturned 創建一個新記錄(通過 Initialize)并在返回時調用 Assign 運算符(如果調用類似于 my1 := ParReturned),然后在賦值后刪除臨時記錄

異常和托管記錄

? 與對象不同,當異常發生時,即使沒有顯式的 try-finally 塊,記錄一般也會被清除。這是一個根本區別,也是托管記錄真正有用的關鍵所在。

procedure ExceptionTest;
beginvar A: TMRE;var B: TMRE;raise Exception.Create('Error Message');
end;

? 在這個過程里,有兩次構造函數調用和兩次析構函數調用。同樣,這也是托管記錄的根本區別和關鍵特征。

托管記錄的數組

? 如果定義了托管記錄的靜態數組,則會在聲明時調用 Initialize 操作符對其進行初始化:

varA1: array[1..5] of TMyRecord; // 在這里調用初始化
beginLog('ArrOfRec');

當超出作用域時,它們就會被全部銷毀。如果定義了托管記錄的動態數組,則在調用初始化代碼時,要確定數組的大小(使用 SetLength):

varA2: array of TMyRecord;
beginLog('ArrOfDyn');SetLength(A2, 5); // 在這里調用初始化

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/696982.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/696982.shtml
英文地址,請注明出處:http://en.pswp.cn/news/696982.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大語言模型LangChain本地知識庫:向量數據庫與文件處理技術的深度整合

文章目錄 大語言模型LangChain本地知識庫:向量數據庫與文件處理技術的深度整合引言向量數據庫在LangChain知識庫中的應用文件處理技術在知識庫中的角色向量數據庫與文件處理技術的整合實踐挑戰與展望結論 大語言模型LangChain本地知識庫:向量數據庫與文件…

【Unity】MySql +Navicat 安裝教程

問題描述 在使用Unity開發的時候,有的時候我們是需要使用Mysql數據庫的,本教程使用的MySql 和Navicat均為免安裝版 ?mysql安裝 1.下載mysql解壓至任意目錄,此處以“C:\mysql-5.6.39-winx64”為例. mysql百度云連接: 鏈接&…

Java的遞歸【詳解】

1.認識遞歸基礎知識 什么是方法遞歸? 遞歸是一種算法,在程序設計語言中廣泛應用。 從形式上說:方法調用自身的形式稱為方法遞歸( recursion)。 遞歸的形式: 直接遞歸:方法自己調用自己。 間接遞…

【監控】Spring Boot+Prometheus+Grafana實現可視化監控

目錄 1.概述 2.spring actuator 3.Prometheus 3.1.介紹 3.2.使用 1.client端的配置 2.server端的配置 4.grafana 5.留個尾巴 1.概述 本文是博主JAVA監控技術系列的第四篇,前面已經聊過了JMX、Spring actuator等技術,本文我們就將依托于Spring …

利用docker一鍵部署LLaMa到自己的Linux服務器,有無GPU都行、可以指定GPU數量、支持界面對話和API調用,離線本地化部署包含模型權重合并

利用docker一鍵部署LLaMa到自己的Linux服務器,有無GPU都行、可以指定GPU數量、支持界面對話和API調用,離線本地化部署包含模型權重合并。兩種方式實現支持界面對話和API調用,一是通過搭建text-generation-webui。二是通過llamma.cpp轉換模型為轉換為 GGUF 格式,使用 quanti…

Leetcode日記 889. 根據前序和后序遍歷構造二叉樹

Leetcode日記 889. 根據前序和后序遍歷構造二叉樹 給定兩個整數數組,preorder 和 postorder ,其中 preorder 是一個具有 無重復 值的二叉樹的前序遍歷,postorder 是同一棵樹的后序遍歷,重構并返回二叉樹。 如果存在多個答案&#…

【Flink集群RPC通訊機制(三)】AkkaRpcActor設計與實現:接收RPC消息以及處理邏輯

文章目錄 1. 創建Receiver2. 進行消息處理 RPC請求發送后接收方的處理邏輯 在RpcEndpoint中創建的RemoteRpcInvocation消息,最終會通過Akka系統傳遞到被調用方。例如TaskExecutor向ResourceManager發送SlotReport請求的時候,會在TaskExecutor中將Resourc…

petalinux_zynq7 驅動DAC以及ADC模塊之二:petalinux

petalinux_zynq7 C語言驅動DAC以及ADC模塊之一:建立IPhttps://blog.csdn.net/qq_27158179/article/details/136234296在上一篇,建立了ADC和DAC兩個IP。這里繼續。本文在 petalinux默認配置的基礎上,添加了python和qt。再編譯出sdk可以給x86主…

汽車智能座艙中 顯示屏市場戰略趨勢分析 中篇

今天主要講講主流車廠顯示屏的趨勢。 主流車廠的中控&液晶儀表屏的尺寸及趨勢匯總 奔馳 奔馳A級 10.2510.25 奔馳C級 12.310.25 奔馳GLA 10.2510.25 奔馳E級 12.312.3 奔馳S級 12.312.8 1、奔馳的儀表幾乎都為液晶儀表,幾乎所有車型都有HUD的選配&#xff…

大功率應用中的厚膜電阻散熱器的設計?

在許多大功率應用中,例如電機和電源,電源電阻器位于主電源線中。它們的目的是防止損壞或提供一定程度的控制。 在這些應用中,電阻器承受恒定的、相對較高的電流。當電流流過電阻器時,它會產生熱量。這種熱能必須消散到環境中&…

1、WEB攻防-通用漏洞SQL注入MYSQL跨庫ACCESS偏移

用途:個人學習筆記,歡迎指正! 前言: 為了網站和數據庫的安全性,MYSQL 內置有 ROOT 最高用戶,劃分等級,每個用戶對應管理一個數據庫,這樣保證無不關聯,從而不會影響到其他…

Autosar-WdgM配置詳解-3

1.11生成代碼解析 1.11.1MasterSWC代碼解析 在MasterSWC的RE_TestRun這個runnable里會調用兩個檢測點函數,我們可以在兩個檢測點函數之間,加入我們所需要監控的代碼。 ?Rte_Call_RPort_StartCheckPoint_CheckpointReached(); ?Rte_Call_RPort_EndCheckPoint_CheckpointReac…

C#串口 Modbus通訊工具類

一、安裝Modbus包 二、創建modbushelper類 1、打開串口 public bool IfCOMOpend; //用于實例內的COM口的狀態 public SerialPort OpenedCOM;//用于手動輸入的COM轉成SERIAL PORT /// <summary> /// 打開串口 /// </summary> /// <param name="COMname&quo…

unity小工具-非實時的值變化監聽器

項目里有代碼專門監聽網絡環境的變化&#xff0c;特別是在下載中&#xff0c;如果遇到斷網或者切換為移動網絡&#xff0c;可能需要觸發提醒等等。這種需求可能是通用的&#xff0c;于是便寫了一個通用的監聽代碼。是 using System; using System.Collections; using System.C…

c++服務器開源項目Tinywebserver運行

c服務器開源項目Tinywebserver運行 一、Tinywebserver介紹二、環境搭建三、構建數據庫四、編譯Tinywebserver五、查看效果 Tinywebserver是github上一個十分優秀的開源項目&#xff0c;幫助初學者學習如何搭建一個服務器。 本文講述如何在使用mysql跟該項目進行連接并將項目運行…

python 層次分析(AHP)

文章目錄 一、算法原理二、案例分析2.1 構建指標層判斷矩陣2.2 求各指標權重2.2.1 算術平均法&#xff08;和積法&#xff09;2.2.2 幾何平均法&#xff08;方根法&#xff09; 2.3 一致性檢驗2.3.1 求解最大特征根值2.3.2 求解CI、RI、CR值2.3.3 一致性判斷 2.4 分別求解方案層…

利用Ubuntu22.04啟動U盤對電腦磁盤進行格式化

概要&#xff1a; 本篇演示利用Ubuntu22.04啟動U盤的Try Ubuntu模式對電腦磁盤進行格式化 一、說明 1、電腦 筆者的電腦品牌是acer(宏碁/宏基) 開機按F2進入BIOS 開機按F12進入Boot Manager 2、Ubuntu22.04啟動U盤 制作方法參考筆者的文章&#xff1a; Ubuntu制作Ubun…

【OpenAI官方課程】第五課:ChatGPT文本轉換Transforming

歡迎來到ChatGPT 開發人員提示工程課程&#xff08;ChatGPT Prompt Engineering for Developers&#xff09;&#xff01;本課程將教您如何通過OpenAI API有效地利用大型語言模型&#xff08;LLM&#xff09;來創建強大的應用程序。 本課程由OpenAI 的Isa Fulford和 DeepLearn…

緩存篇—緩存雪崩

什么是緩存雪崩 通常我們為了保證緩存中的數據與數據庫中的數據一致性&#xff0c;會給 Redis 里的數據設置過期時間&#xff0c;當緩存數據過期后&#xff0c;用戶訪問的數據如果不在緩存里&#xff0c;業務系統需要重新生成緩存&#xff0c;因此就會訪問數據庫&#xff0c;并…

QEMU源碼全解析 —— virtio(22)

接前一篇文章&#xff1a;QEMU源碼全解析 —— virtio&#xff08;21&#xff09; 前幾回講解了virtio驅動的加載。本回開始講解virtio驅動的初始化。 在講解virtio驅動的初始化之前&#xff0c;先要介紹virtio配置的函數集合變量virtio_pci_config_ops。實際上前文書也有提到…