首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

Managed C++ 中刪除托管物件與封裝原生庫實戰

     編輯:VC 教學

在 Managed C++ 中,是否可以使用 delete 運算子銷毀托管物件?答案是肯定的,前提是你理解 delete 僅會呼叫物件的解構函式,並不會釋放記憶體——記憶體回收仍由垃圾收集器(GC)負責。且解構函式必須顯式定義。

delete 行為示範

以下程式定義帶有解構函式的托管類別,並在主程式中建立兩個實例:第一個顯式 delete,第二個交由 GC 回收。

// 輸出結果:
// Begin main
// ManagedClass(04A712D4)::ctor
// ManagedClass(04A712D4)::dtor
// ManagedClass(04A712E0)::ctor
// End main
// ManagedClass(04A712E0)::dtor

可見 delete 會立即觸發解構函式,而未刪除的物件則在程式結束、GC 運行時才被終結。

MSIL 層面解析

編譯器會為解構函式產生兩個方法:

  • Finalize:包含使用者實作的清理代碼。
  • __dtor:呼叫 System.GC::SuppressFinalize 後再呼叫 Finalize。

delete 陳述式編譯為對 __dtor 的呼叫。若使用 /FAs 產生組件清單可見:

; delete pmc;
ldloc.0 ; _pmc$
call ??1ManagedClass@@[email protected]

為何要用 delete?

唯一理由是確定性釋放非托管資源(如檔案控制碼、資料庫連線、GDI 物件)。若類別持有這類資源,應在解構函式中關閉釋放,並在使用完畢時呼叫 delete。

更佳作法:實作 IDisposable 模式

解構函式清理法有缺陷:其他 .NET 語言(C#、VB.NET)無 delete 運算子,無法顯式呼叫清理代碼。標準做法是實作 IDisposable 介面,並搭配 auto_dispose(Managed C++)或 using 區塊(C#)使用。參考 Tomas Restrepo 在 MSDN Magazine 2002 年 2 月號文章《Tips and Tricks to Bolster Your Managed C++ Code in Visual Studio .NET》。

Visual C++ 2005 後行為有變更,詳見 Andy Rich 討論《Deterministic Finalization IV - Benefits, part II》及 C++/CLI 標準規範。

封裝原生鏈結串列為托管集合

假設有原生函式回傳鏈結串列:

struct blah { int a, b; char *a, *b; struct blah *next; };
struct blah *getmystruct();
void freemystruct(struct blah *b);

無法直接用 dllimport 映射為托管型別。需撰寫封裝器,將原生串列轉換為 ArrayList 等托管集合。

三模組架構示範

  1. ListLib.cpp:原生 DLL,實作 AllocateList、FreeList 操作 NativeNode 結構。
  2. ListWrap.cpp:Managed C++ 封裝層,定義 ManagedNode 類別(以 String* 取代 char*,無 next 指標),並實作 CreateList 方法:呼叫 AllocateList、逐節點轉換、加入 ArrayList、釋放原生節點。
  3. C# 測試程式:直接呼叫 ListWrap.CreateList() 取得 ArrayList,完全隱藏原生細節。

關鍵轉換代碼片段

// ManagedNode 定義
public __gc class ManagedNode {
public:
    int a, b;
    String* str;
};

// 轉換核心邏輯
ArrayList* CreateList(int count) {
    NativeNode* nativeList = AllocateList(count);
    ArrayList* managedList = new ArrayList();
    for (NativeNode* nn = nativeList; nn != 0; nn = nn->next) {
        ManagedNode* mn = new ManagedNode();
        mn->a = nn->a;
        mn->b = nn->b;
        mn->str = nn->str; // char* 到 String* 隱式轉換
        managedList->Add(mn);
    }
    FreeList(nativeList); // 釋放原生記憶體
    return managedList;
}

此法完全封裝原生 DLL,C# 客戶端無需處理 IntPtr、無需匯入 DLL,介面純粹托管,安全且易用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved