共用方式為


管理物件的存留期

我們尚未提及的 COM 介面有一個規則。 每個 COM 介面都必須直接或間接繼承自名為 IUnknown 的介面。 此介面提供所有 COM 物件必須支援的一些基準功能。

IUnknown介面會定義三種方法:

QueryInterface方法可讓程式在執行時間查詢物件的功能。 我們將在下一個主題中進一步討論 ,詢問物件以取得介面 AddRefRelease方法可用來控制物件的存留期。 這是本主題的主題。

參考計數

程式可能執行的任何其他動作,在某個時間點,它將會配置和釋放資源。 配置資源很簡單。 瞭解何時釋放資源是硬式的,特別是當資源的存留期超出目前範圍時。 此問題對 COM 而言並不是唯一的。 配置堆積記憶體的任何程式都必須解決相同的問題。 例如,C++ 會使用自動解構函式,而 C# 和 JAVA 則使用垃圾收集。 COM 使用稱為 參考計數的方法。

每個 COM 物件都會維護內部計數。 這稱為參考計數。 參考計數會追蹤目前作用中物件的參考數目。 當參考數目捨棄為零時,物件會刪除本身。 最後一個部分值得重複:物件會自行刪除。 程式永遠不會明確刪除 物件。

以下是參考計數的規則:

  • 第一次建立物件時,其參考計數為 1。 此時,程式具有 物件的單一指標。
  • 程式可以複製 (複製指標) ,以建立新的參考。 複製指標時,您必須呼叫 物件的 AddRef 方法。 這個方法會將參考計數遞增一個。
  • 當您完成使用 物件的指標時,必須呼叫 ReleaseRelease方法會將參考計數遞減一。 它也會使指標失效。 呼叫 Release之後,請勿再次使用指標。 (如果您有相同物件的其他指標,您可以繼續使用這些 pointers.)
  • 當您以每個指標呼叫 Release 時,物件的物件參考計數會達到零,而且物件會刪除本身。

下圖顯示簡單但典型的案例。

此圖顯示參考計數的簡單案例。

程式會建立 物件,並將指標儲存 (p) 物件。 此時,參考計數為 1。 當程式使用指標完成時,它會呼叫 Release。 參考計數會遞減為零,而且物件會自行刪除。 現在 p 無效。 針對任何進一步的方法呼叫使用 p 是一個錯誤。

下圖顯示更複雜的範例。

顯示參考計數的圖例

在這裡,程式會建立 物件,並儲存指標 p,如同先前一樣。 接下來,程式會將 p 複製到新的變數 q。 此時,程式必須呼叫 AddRef 以遞增參考計數。 參考計數現在是 2,而且物件有兩個有效的指標。 現在假設程式已完成使用 p。 程式會呼叫 Release,參考計數會移至 1,而 p 已不再有效。 不過, q 仍然有效。 稍後,程式會使用 q完成。 因此,它會再次呼叫 Release 。 參考計數會移至零,而且物件會刪除本身。

您可能會想知道程式為何會複製 p。 有兩個主要原因:首先,您可能會想要將指標儲存在資料結構中,例如清單。 其次,您可能想要將指標保留在原始變數的目前範圍之外。 因此,您會將它複製到具有更廣泛範圍的新變數。

參考計數的其中一個優點是,您可以跨不同程式碼區段共用指標,而不需要協調各種程式碼路徑來刪除物件。 相反地,當程式碼路徑使用 物件完成時,每個程式碼路徑只會呼叫 Release 。 物件會在正確的時間處理刪除本身。

範例

以下是 [ 開啟] 對話方塊範例 中的程式碼。

HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED |
    COINIT_DISABLE_OLE1DDE);

if (SUCCEEDED(hr))
{
    IFileOpenDialog *pFileOpen;

    hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
            IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

    if (SUCCEEDED(hr))
    {
        hr = pFileOpen->Show(NULL);
        if (SUCCEEDED(hr))
        {
            IShellItem *pItem;
            hr = pFileOpen->GetResult(&pItem);
            if (SUCCEEDED(hr))
            {
                PWSTR pszFilePath;
                hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
                if (SUCCEEDED(hr))
                {
                    MessageBox(NULL, pszFilePath, L&quot;File Path&quot;, MB_OK);
                    CoTaskMemFree(pszFilePath);
                }
                pItem->Release();
            }
        }
        pFileOpen->Release();
    }
    CoUninitialize();
}

參考計數會在此程式碼的兩個位置發生。 首先,如果程式成功建立 Common Item Dialog 物件,則必須在pFileOpen指標上呼叫Release

hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, 
        IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

if (SUCCEEDED(hr))
{
    // ...
    pFileOpen->Release();
}

其次,當GetResult方法傳回IShellItem介面的指標時,程式必須在pItem指標上呼叫Release

hr = pFileOpen->GetResult(&pItem);

if (SUCCEEDED(hr))
{
    // ...
    pItem->Release();
}

請注意,在這兩種情況下, Release 呼叫是在指標超出範圍之前發生的最後一件事。 另請注意,只有在測試HRESULT以取得成功之後,才會呼叫Release。 例如,如果 呼叫 CoCreateInstance 失敗, pFileOpen 指標無效。 因此,呼叫指標上的 Release 會是錯誤。

下一個

要求物件取得介面