小提示
使用智慧指標代替手動 AddRef/Release。 手動參考計數是 COM 錯誤的頭號根源(物件洩漏、釋放後使用、重複釋放)。 現代 C++ 程式碼應該使用能自動處理參考計數的智慧指標:
#include <wrl/client.h> // Microsoft::WRL::ComPtr
// ComPtr automatically calls AddRef on copy and Release on destruction
Microsoft::WRL::ComPtr<IStream> stream;
HRESULT hr = CreateStreamOnHGlobal(nullptr, TRUE, &stream);
if (FAILED(hr)) return hr;
// Pass to another interface — use .Get() for raw pointer
// Note: operator& on ComPtr asserts if it already holds a value.
// Use .ReleaseAndGetAddressOf() if the ComPtr may be non-null.
Microsoft::WRL::ComPtr<IUnknown> unknown;
hr = stream.As(&unknown); // Safe QueryInterface — no manual Release needed
替代方案:
-
winrt::com_ptr<T>— C++/WinRT 智慧指標(需 C++17,與 WinRT 投影整合) -
wil::com_ptr_nothrow<T>— Windows 實作函式庫(無例外替代方案)
以下說明的手動 AddRef/Release 規則仍適用於理解 COM 內部運作及實作 COM 伺服器,但用戶端程式碼很少直接呼叫這些方法。
在傳統的物件系統中,物件的生命週期——也就是與物件的建立和銷毀有關的問題——是由語言(或語言執行階段)隱含地處理,或由應用程式設計師明確地處理。
在一個由重複使用的元件所組成、持續演變且以去中心化方式建構的系統中,已不能再說任何用戶端,甚至任何程式設計師,總是「知道」如何處理元件的生命週期。 對於擁有適當安全權限的客戶端來說,透過簡單的請求建立物件仍然相對容易,但物件刪除則是另一回事。 物件何時不再需要並應該刪除,並不一定明確。 (熟悉垃圾回收程式環境(如 Java)的讀者可能會有不同意見;然而,Java 物件不會跨越機器或程序邊界,因此垃圾回收僅限於單一程序空間內的物件。此外,Java 強制使用單一程式語言。)即使原始用戶端已經完成物件處理,也無法簡單地關閉該物件,因為其他用戶端可能仍持有該物件的參考。
確保某個物件已不再需要的一種方式,是完全依賴底層通訊通道,在所有與某個跨處理程序或跨通道物件的連線都消失時通知系統。 然而,採用此方法的方案因多種原因而不可接受。 其中一個問題是,這可能需要跨程序/跨網路程式設計模型與單程序程式設計模型有重大差異。 在跨程序/跨網路程式設計模型中,通訊系統會提供物件壽命管理所需的鉤子;而在單程序程式設計模型中,物件直接連接,沒有任何通訊通道介入。 另一個問題是,這種方案還可能導致系統自家提供的軟體層,干擾在過程中元件的效能。 此外,基於明確監控的機制不太可能擴展到數千甚至數百萬個物件。
COM為這組問題提供了可擴展且分散式的方法。 客戶端會告訴物件何時使用、何時使用完畢,物件則在不再需要時自行刪除。 此方法要求所有物件都對自身進行參考計數。 像 Java 這類本身有其生命週期管理機制(如垃圾回收)的程式語言,可以利用 COM 的參考計數來實作並內部使用 COM 物件,讓程式設計師免於處理這些問題。
正如應用程式在已配置的記憶體不再使用時必須釋放它一樣,物件的用戶端程式也有責任在不再需要該物件時,釋放對該物件的參照。 在物件導向系統中,客戶端只能透過給予物件指令來釋放自己。
當物件不再被使用時,將其解除配置是很重要的。 困難在於判斷何時適合釋放一個物件。 這對於自動變數(堆疊上分配的變數)來說很簡單——它們不能在宣告區塊外使用,因此當區塊結束時,編譯器會釋放它們。 對於動態分配的 COM 物件,物件的用戶端決定何時不再需要使用該物件——尤其是可能同時被多個用戶端使用的本地或遠端物件。 物件必須等到所有客戶端都用畢該物件後,才能自行釋放。 由於 COM 物件是透過介面指標操作,且可被不同程序中的物件或其他機器使用,系統無法追蹤物件的用戶端。
COM判斷何時適合釋放物件的方法就是手動參照計數。 每個物件都會維持一個參考計數,用來追蹤連接它的用戶端數量——也就是說,在任何用戶端中,有多少指向其介面的指標數量。
如需詳細資訊,請參閱下列主題:
相關主題