C++/WinRT 中的強式和弱式參考Strong and weak references in C++/WinRT

Windows 執行階段是參考計數式系統;在這樣的系統中,請務必了解強式和弱式參考 (以及不是這兩者的參考,例如隱含 this 指標) 的重要性以及之間的區別。The Windows Runtime is a reference-counted system; and in such a system it's important for you to know about the significance of, and distinction between, strong and weak references (and references that are neither, such as the implicit this pointer). 您將在本主題中了解,針對順利執行的可靠系統和針對會意外當機的系統,正確管理這些參考的方式可能會有差異。As you'll see in this topic, knowing how to manage these references correctly can mean the difference between a reliable system that runs smoothly, and one that crashes unpredictably. 藉由提供可深入支援語言投影的協助程式函式,C++/WinRT 可讓您輕鬆且正確地建置更複雜的系統。By providing helper functions that have deep support in the language projection, C++/WinRT meets you halfway in your work of building more complex systems simply and correctly.

安全地存取類別成員協同程式中的 this 指標Safely accessing the this pointer in a class-member coroutine

如需有關協同程式的詳細資訊和程式碼範例,請參閱使用 C++/WinRT 的並行和非同步作業For more info about coroutines, and code examples, see Concurrency and asynchronous operations with C++/WinRT.

下方列出的程式碼會示範典型的協同程式範例,其中一個類別有一個成員函式。The code listing below shows a typical example of a coroutine that's a member function of a class. 您可以複製此範例並貼到新 Windows 主控台應用程式 (C++/WinRT) 專案中的指定檔案。You can copy-paste this example into the specified files in a new Windows Console Application (C++/WinRT) project.

// pch.h
#pragma once
#include <iostream>
#include <winrt/Windows.Foundation.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"

using namespace winrt;
using namespace Windows::Foundation;
using namespace std::chrono_literals;

struct MyClass : winrt::implements<MyClass, IInspectable>
{
    winrt::hstring m_value{ L"Hello, World!" };

    IAsyncOperation<winrt::hstring> RetrieveValueAsync()
    {
        co_await 5s;
        co_return m_value;
    }
};

int main()
{
    winrt::init_apartment();

    auto myclass_instance{ winrt::make_self<MyClass>() };
    auto async{ myclass_instance->RetrieveValueAsync() };

    winrt::hstring result{ async.get() };
    std::wcout << result.c_str() << std::endl;
}

MyClass::RetrieveValueAsync 會花一些時間運作,並在最終傳回一份 MyClass::m_value 資料成員。MyClass::RetrieveValueAsync spends some time working, and eventually it returns a copy of the MyClass::m_value data member. 呼叫 RetrieveValueAsync 會建立非同步物件,而該物件具有隱含的 this 指標 (最終可透過此指標存取 m_value)。Calling RetrieveValueAsync causes an asynchronous object to be created, and that object has an implicit this pointer (through which, eventually, m_value is accessed).

請記住,在協同程式中,執行是同步的,直到在第一個暫停點上將控制項傳回呼叫端為止。Remember that, in a coroutine, execution is synchronous up until the first suspension point, where control is returned to the caller. RetrieveValueAsync 中,第一個 co_await 是第一個暫停點。In RetrieveValueAsync, the first co_await is the first suspension point. 協同程式恢復之前 (在此案例中,大約是五秒之後),隱含的 this 指標 (可透過該指標存取 m_value) 上可能會發生任何事。By the time the coroutine resumes (around five seconds later, in this case), anything might have happened to the implicit this pointer through which we access m_value.

以下是完整的事件順序。Here's the full sequence of events.

  1. MyClass 的執行個體會在 之中建立 (myclass_instance)。In main, an instance of MyClass is created (myclass_instance).
  2. async 物件建立,並指向 (透過其中的 this ) myclass_instanceThe async object is created, pointing (via its this) to myclass_instance.
  3. winrt::Windows::Foundation::IAsyncAction::get 函式會觸及其第一個暫停點、封鎖幾秒鐘的時間,然後傳回 RetrieveValueAsync 的結果。The winrt::Windows::Foundation::IAsyncAction::get function hits its first suspension point, blocks for a few seconds, and then returns the result of RetrieveValueAsync.
  4. RetrieveValueAsync 傳回 this->m_value 的值。RetrieveValueAsync returns the value of this->m_value.

只要 this 仍有效,步驟 4 就是安全的。Step 4 is safe only as long as this remains valid.

但是,如果在非同步作業完成之前,類別執行個體就損毀了呢?But what if the class instance is destroyed before the async operation completes? 有各種狀況會造成類別執行個體在非同步方法完成之前脫離範圍。There are all kinds of ways the class instance could go out of scope before the asynchronous method has completed. 但是,我們可以藉由將類別執行個體設定為 nullptr 來模擬此狀況。But we can simulate it by setting the class instance to nullptr.

int main()
{
    winrt::init_apartment();

    auto myclass_instance{ winrt::make_self<MyClass>() };
    auto async{ myclass_instance->RetrieveValueAsync() };
    myclass_instance = nullptr; // Simulate the class instance going out of scope.

    winrt::hstring result{ async.get() }; // Behavior is now undefined; crashing is likely.
    std::wcout << result.c_str() << std::endl;
}

在損毀類別執行個體的指標後方,這看起來像是我們沒有直接再次參考該執行個體。After the point where we destroy the class instance, it looks like we don't directly refer to it again. 但是非同步物件當然有指向它的 this 指標,而且嘗試使用該指標來複製類別執行個體中儲存的值。But of course the asynchronous object has a this pointer to it, and tries to use that to copy the value stored inside the class instance. 協同程式是成員函式,而且其預期能夠順利使用 this 指標。The coroutine is a member function, and it expects to be able to use its this pointer with impunity.

經由此程式碼變更,我們會在步驟 4 遇到問題,因為類別執行個體已損毀,而 this 已不再有效。With this change to the code, we run into a problem in step 4, because the class instance has been destroyed, and this is no longer valid. 只要非同步物件嘗試存取類別執行個體內的變數,就會發生損毀 (或是執行某個未完全未定義的動作)。As soon as the asynchronous object attempts to access the variable inside the class instance, it will crash (or do something entirely undefined).

解決方法是將非同步作業—協同程式—自己的強式參考提供給類別執行個體。The solution is to give the asynchronous operation—the coroutine—its own strong reference to the class instance. 如同目前所撰寫的內容,協同程式可有效保留指向類別執行個體的原始 this 指標;但這並不足以讓類別執行個體存留下來。As currently written, the coroutine effectively holds a raw this pointer to the class instance; but that's not enough to keep the class instance alive.

若要讓類別執行個體存留下來,應變更 RetrieveValueAsync 的實作,如下列所示。To keep the class instance alive, change the implementation of RetrieveValueAsync to that shown below.

IAsyncOperation<winrt::hstring> RetrieveValueAsync()
{
    auto strong_this{ get_strong() }; // Keep *this* alive.
    co_await 5s;
    co_return m_value;
}

C++/WinRT 類別會直接或間接從 winrt::implements 範本衍生。A C++/WinRT class directly or indirectly derives from the winrt::implements template. 因此,C++/WinRT 物件可以呼叫其受保護的 implements::get_strong 成員函式,以擷取其 this 指標的強式參考。Because of that, the C++/WinRT object can call its implements::get_strong protected member function to retrieve a strong reference to its this pointer. 請注意,您不需要在上述程式碼範例中確實使用 strong_this 變數,只要呼叫 get_strong 來累加 C++/WinRT 物件的參考計數,並讓其隱含的 this 指標保持有效即可。Note that there's no need to actually use the strong_this variable in the code example above; simply calling get_strong increments the C++/WinRT object's reference count, and keeps its implicit this pointer valid.

重要

由於 get_strongwinrt::implements 結構範本的成員函式,因此您只能從直接或間接衍生自 winrt::implements 的類別 (例如 C++/WinRT 類別) 中呼叫該函式。Because get_strong is a member function of the winrt::implements struct template, you can call it only from a class that directly or indirectly derives from winrt::implements, such as a C++/WinRT class. 如需有關衍生自 winrt::implements 的資訊和範例,請參閱使用 C++/WinRT 撰寫 APIFor more info about deriving from winrt::implements, and examples, see Author APIs with C++/WinRT.

此方法解決了我們先前在步驟 4 時遇到的問題。This resolves the problem that we previously had when we got to step 4. 即使指向類別執行個體的所有其他參考都消失,協同程式已有保證其相依性穩定的預防措施。Even if all other references to the class instance disappear, the coroutine has taken the precaution of guaranteeing that its dependencies are stable.

如果強式參考並不適合,您可以改為呼叫 implements::get_weak 來對 this 擷取弱式參考。If a strong reference isn't appropriate, then you can instead call implements::get_weak to retrieve a weak reference to this. 請在確認您可以擷取強式參考後,再存取 this 。Just confirm that you can retrieve a strong reference before accessing this. 同樣地,get_weakwinrt::implements 結構範本的成員函式。Again, get_weak is a member function of the winrt::implements struct template.

IAsyncOperation<winrt::hstring> RetrieveValueAsync()
{
    auto weak_this{ get_weak() }; // Maybe keep *this* alive.

    co_await 5s;

    if (auto strong_this{ weak_this.get() })
    {
        co_return m_value;
    }
    else
    {
        co_return L"";
    }
}

在上述範例中,在未保留強式參考的情況下,弱式參考並不能避免類別執行個體發生損毀。In the example above, the weak reference doesn't keep the class instance from being destroyed when no strong references remain. 但可以讓您確認是否能在存取成員變數之前取得強式參考。But it gives you a way of checking whether a strong reference can be acquired before accessing the member variable.

使用事件處理委派安全地存取 this 指標Safely accessing the this pointer with an event-handling delegate

案例The scenario

如需有關事件處理的一般資訊,請參閱藉由在 C++/WinRT 中使用委派來處理事件For general info about event-handling, see Handle events by using delegates in C++/WinRT.

在上一節中,我們已指出協同程式和並行存取方面的潛在存留期問題。The previous section highlighted potential lifetime issues in the areas of coroutines and concurrency. 但是,如果您使用物件的成員函式,或是從物件成員函式裡的 lambda 函式中處理一個事件,您會需要考量事件 (處理事件的物件) 和事件來源 (引發事件的物件) 的相對存留時間。But, if you handle an event with an object's member function, or from within a lambda function inside an object's member function, then you need to think about the relative lifetimes of the event recipient (the object handling the event) and the event source (the object raising the event). 讓我們來看看一些程式碼範例。Let's look at some code examples.

下方列出的程式碼會先定義簡單的 EventSource 類別,而其產生的一般事件會由任何已加入類別的委派來處理。The code listing below first defines a simple EventSource class, which raises a generic event that's handled by any delegates that have been added to it. 此範例事件會使用 Windows::Foundation::EventHandler 委派類別,但此問題和補救方法適用於所有的委派類型。This example event happens to use the Windows::Foundation::EventHandler delegate type, but the issues and remedies here apply to any and all delegate types.

然後,EventRecipient 類別會以 lambda 函式的形式為 EventSource::Event 事件提供處理常式。Then, the EventRecipient class provides a handler for the EventSource::Event event in the form of a lambda function.

// pch.h
#pragma once
#include <iostream>
#include <winrt/Windows.Foundation.h>

// main.cpp : Defines the entry point for the console application.
#include "pch.h"

using namespace winrt;
using namespace Windows::Foundation;

struct EventSource
{
    winrt::event<EventHandler<int>> m_event;

    void Event(EventHandler<int> const& handler)
    {
        m_event.add(handler);
    }

    void RaiseEvent()
    {
        m_event(nullptr, 0);
    }
};

struct EventRecipient : winrt::implements<EventRecipient, IInspectable>
{
    winrt::hstring m_value{ L"Hello, World!" };

    void Register(EventSource& event_source)
    {
        event_source.Event([&](auto&& ...)
        {
            std::wcout << m_value.c_str() << std::endl;
        });
    }
};

int main()
{
    winrt::init_apartment();

    EventSource event_source;
    auto event_recipient{ winrt::make_self<EventRecipient>() };
    event_recipient->Register(event_source);
    event_source.RaiseEvent();
}

模式是事件收件者會有相依於其 this 指標的 lambda 事件處理常式。The pattern is that the event recipient has a lambda event handler with dependencies on its this pointer. 只要事件收件者的存留期比事件來源長,處理常式的存留期就會超過這些相依性的存留期。Whenever the event recipient outlives the event source, it outlives those dependencies. 而且在這些情況下,該模式通常會順利運作。And in those cases, which are common, the pattern works well. 這些案例中有些很明顯,例如,UI 網頁藉由頁面上的控制項處理事件時。Some of these cases are obvious, such as when a UI page handles an event raised by a control that's on the page. 頁面存留期會超過按鈕存留期—因此,處理常式的存留期也會超過按鈕。The page outlives the button—so, the handler also outlives the button. 任何時候都是如此,收件者擁有來源 (例如,做為資料成員),或收件者與來源屬同層級,且由其他物件擁有。This holds true any time the recipient owns the source (as a data member, for example), or any time the recipient and the source are siblings and directly owned by some other object.

如果您確定有一個案例,其中處理常式不會超越其所依賴的 this ,您便可以正常擷取 this ,而不需要考量強式或弱式存留時間。When you're sure you have a case where the handler won't outlive the this that it depends on, then you can capture this normally, without consideration for strong or weak lifetime.

但仍有案例,其中 this 的存留期不會超過其在處理常式中的使用時間 (包括由非同步動作與作業引發的完成和進行中事件的處理常式),而您必須知道如何處理這些狀況。But there are still cases where this doesn't outlive its use in a handler (including handlers for completion and progress events raised by asynchronous actions and operations), and it's important to know how to deal with them.

  • 當事件來源「同步」 引發其事件時,您可以撤銷您的處理常式並確信您不會再收到任何事件。When an event source raises its events synchronously, you can revoke your handler and be confident that you won't receive any more events. 但針對非同步事件,即使在撤銷 (尤其是在建構函式內撤銷) 之後,執行中的事件可能會在開始解構之後到達您的物件。But for asynchronous events, even after revoking (and especially when revoking within the destructor), an in-flight event might reach your object after it has started destructing. 在解構前找到可取消訂閱的地方可能會減輕問題,但要繼續閱讀以取得健全的解決方案。Finding a place to unsubscribe prior to destruction might mitigate the issue, but continue reading for a robust solution.
  • 如果您正撰寫協同程式來實作非同步方法,就有可能發生此狀況。If you're authoring a coroutine to implement an asynchronous method, then it's possible.
  • 在少數使用特定 XAML UI 架構物件 (例如,SwapChainPanel) 的案例中有可能發生 (如果收件者不需要從事件來源取消註冊,便能完成的話)。In rare cases with certain XAML UI framework objects (SwapChainPanel, for example), then it's possible, if the recipient is finalized without unregistering from the event source.

問題The issue

下一版的 main 函式在此會模擬當事件來源仍在產生事件時,事件收件者卻發生損毀 (或許是脫離範圍) 時的狀況。This next version of the main function simulates what happens when the event recipient is destroyed (perhaps it goes out of scope) while the event source is still raising events.

int main()
{
    winrt::init_apartment();

    EventSource event_source;
    auto event_recipient{ winrt::make_self<EventRecipient>() };
    event_recipient->Register(event_source);
    event_recipient = nullptr; // Simulate the event recipient going out of scope.
    event_source.RaiseEvent(); // Behavior is now undefined within the lambda event handler; crashing is likely.
}

事件收件者雖發生損毀,但其中的 lambda 事件處理常式仍訂閱 Event 事件。The event recipient is destroyed, but the lambda event handler within it is still subscribed to the Event event. 事件產生時,lambda 會嘗試對該階段中無效的 this 指標取值 (dereference)。When that event is raised, the lambda attempts to dereference the this pointer, which is at that point invalid. 因此,處理常式 (或協同程式的接續) 中嘗試使用該指標的程式碼會產生存取違規。So, an access violation results from code in the handler (or in a coroutine's continuation) attempting to use it.

重要

如果您遇到這類情形,不論擷取的this 物件是否能存留得此擷取作業久,您都需要考量 this 物件的存留時間。If you encounter a situation like this, then you'll need to think about the lifetime of the this object; and whether or not the captured this object outlives the capture. 如果物件沒有存留得比擷取作業久,則使用強式或弱式參考來加以擷取,我們將在下方示範此操作。If it doesn't, then capture it with a strong or a weak reference, as we'll demonstrate below.

或—如果您的案例適合的話,且如果執行緒考量甚至可行—則另一個方法是,在收件者完成事件後,或在收件者解構函式中,撤銷處理常式。Or—if it makes sense for your scenario, and if threading considerations make it even possible—then another option is to revoke the handler after the recipient is done with the event, or in the recipient's destructor. 請參閱撤銷已註冊的委派See Revoke a registered delegate.

這是我們將註冊處理常式的方式。This is how we're registering the handler.

event_source.Event([&](auto&& ...)
{
    std::wcout << m_value.c_str() << std::endl;
});

lambda 會自動依據參考來擷取任何區域變數。The lambda automatically captures any local variables by reference. 因此,針對此範例,我們可以同樣地撰寫下列內容。So, for this example, we could equivalently have written this.

event_source.Event([this](auto&& ...)
{
    std::wcout << m_value.c_str() << std::endl;
});

在這兩種情況下,我們都只擷取原始 this 指標。In both cases, we're just capturing the raw this pointer. 這並不會影響參考計數,因此無法避免目前物件發生損毀。And that has no effect on reference-counting, so nothing is preventing the current object from being destroyed.

解決方案The solution

解決方案是擷取強式參考 (或者,如我們所見,若弱式參考更為適當,則擷取之)。The solution is to capture a strong reference (or, as we'll see, a weak reference if that's more appropriate). 強式參考「會」 累加參考計數,並且「會」 讓目前物件存留下來。A strong reference does increment the reference count, and it does keep the current object alive. 您只需宣告擷取變數 (在此範例中稱為 strong_this),並藉由對 implements::get_strong 發出呼叫來將其初始化,此呼叫會擷取我們的 this 指標的強式參考。You just declare a capture variable (called strong_this in this example), and initialize it with a call to implements::get_strong, which retrieves a strong reference to our this pointer.

重要

由於 get_strongwinrt::implements 結構範本的成員函式,因此您只能從直接或間接衍生自 winrt::implements 的類別 (例如 C++/WinRT 類別) 中呼叫該函式。Because get_strong is a member function of the winrt::implements struct template, you can call it only from a class that directly or indirectly derives from winrt::implements, such as a C++/WinRT class. 如需有關衍生自 winrt::implements 的資訊和範例,請參閱使用 C++/WinRT 撰寫 APIFor more info about deriving from winrt::implements, and examples, see Author APIs with C++/WinRT.

event_source.Event([this, strong_this { get_strong()}](auto&& ...)
{
    std::wcout << m_value.c_str() << std::endl;
});

您甚至可以略過目前物件的自動擷取,並透過擷取變數存取資料成員 (而非透過隱含的 this )。You can even omit the automatic capture of the current object, and access the data member through the capture variable instead of via the implicit this.

event_source.Event([strong_this { get_strong()}](auto&& ...)
{
    std::wcout << strong_this->m_value.c_str() << std::endl;
});

如果強式參考並不適合,您可以改為呼叫 implements::get_weak 來對 this 擷取弱式參考。If a strong reference isn't appropriate, then you can instead call implements::get_weak to retrieve a weak reference to this. 弱式參考「不會」 讓目前的物件保持運作。A weak reference does not keep the current object alive. 所以,在確認您仍可以從弱式參考擷取強式參考後,再存取成員。So, just confirm that you can still retrieve a strong reference from the weak reference before accessing members.

event_source.Event([weak_this{ get_weak() }](auto&& ...)
{
    if (auto strong_this{ weak_this.get() })
    {
        std::wcout << strong_this->m_value.c_str() << std::endl;
    }
});

如果您擷取原始指標,則必須確保讓指向的物件保持運作。If you capture a raw pointer, then you'll need to make sure you keep the pointed-to object alive.

如果您使用成員函式作為委派If you use a member function as a delegate

如同 lambda 函式,這些原則也適用於使用成員函式作為您的委派。As well as lambda functions, these principles also apply to using a member function as your delegate. 由於語法不同,因此我們來看看一些程式碼。The syntax is different, so let's look at some code. 首先,以下是可能不安全的成員函式事件處理常式,並且正在使用原始 this 指標。First, here's the potentially unsafe member function event handler, using a raw this pointer.

struct EventRecipient : winrt::implements<EventRecipient, IInspectable>
{
    winrt::hstring m_value{ L"Hello, World!" };

    void Register(EventSource& event_source)
    {
        event_source.Event({ this, &EventRecipient::OnEvent });
    }

    void OnEvent(IInspectable const& /* sender */, int /* args */)
    {
        std::wcout << m_value.c_str() << std::endl;
    }
};

這是參考物件和其成員函式的慣用標準方式。This is the standard, conventional way to refer to an object and its member function. 若要讓此方式變得安全,您可以在註冊處理常式時建立強式或弱式參考—從 Windows SDK 的 10.0.17763.0 (Windows 10 1809 版) 開始—。To make this safe, you can—as of version 10.0.17763.0 (Windows 10, version 1809) of the Windows SDK—establish a strong or a weak reference at the point where the handler is registered. 此時,我們已知事件的收件者物件仍在執行。At that point, the event recipient object is known to be still alive.

若要建立強式參考,只需要呼叫 get_strong 來取代原始 this 指標。For a strong reference, just call get_strong in place of the raw this pointer. C++/ WinRT 可確保產生的委派會保留目前物件的強式參考。C++/WinRT ensures that the resulting delegate holds a strong reference to the current object.

event_source.Event({ get_strong(), &EventRecipient::OnEvent });

擷取強式參考表示只有在取消註冊處理常式且所有未完成的回呼都已傳回後,您的物件才有資格解構。Capturing a strong reference means that your object will become eligible for destruction only after the handler has been unregistered and all outstanding callbacks have returned. 不過,該保證只有在引發事件時才有效。However, that guarantee is valid only at the time the event is raised. 如果您的事件處理常式為非同步,您必須在第一個暫停點之前,為您的協同程式提供類別執行個體的強式參考 (如需詳細資訊以及程式碼,請參閱本主題稍早的安全地存取類別成員協同程式中的 this 指標一節)。If your event handler is asynchronous, then you'll have to give your coroutine a strong reference to the class instance before the first suspension point (for details, and code, see the Safely accessing the this pointer in a class-member coroutine section earlier in this topic). 但是,這會在事件來源與您的物件之間建立循環參考,因此您必須藉由撤銷事件來明確中斷。But that creates a circular reference between the event source and your object, so you need to explicitly break that by revoking your event.

若要建立弱式參考,請呼叫 get_weakFor a weak reference, call get_weak. C++/ WinRT 可確保產生的委派會保留弱式參考。C++/WinRT ensures that the resulting delegate holds a weak reference. 在最後一刻,委派會在幕後嘗試讓弱式參考變成強式參考,並且只會在成功時呼叫成員函式。At the last minute, and behind the scenes, the delegate attempts to resolve the weak reference to a strong one, and only calls the member function if it's successful.

event_source.Event({ get_weak(), &EventRecipient::OnEvent });

如果委派「的確」 呼叫您的成員函式,則 C++/WinRT 會讓您的物件保持運作,直到您的處理常式傳回為止。If the delegate does call your member function, then C++/WinRT will keep your object alive until your handler returns. 不過,如果您的處理常式為非同步,則它會在暫停點傳回,因此您必須在第一個暫停點之前,為您的協同程式提供類別執行個體的強式參考。However, if your handler is asynchronous, then it returns at suspension points, and so you'll have to give your coroutine a strong reference to the class instance before the first suspension point. 同樣地,如需詳細資訊,請參閱本主題稍早的安全地存取類別成員協同程式中的 this 指標一節。Again, for more info, see Safely accessing the this pointer in a class-member coroutine section earlier in this topic.

弱式參考範例會使用 SwapChainPanel::CompositionScaleChangedA weak reference example using SwapChainPanel::CompositionScaleChanged

在此程式碼範例中,我們會以另一種弱式參考形式來使用 SwapChainPanel::CompositionScaleChanged 事件。In this code example, we use the SwapChainPanel::CompositionScaleChanged event by way of another illustration of weak references. 程式碼會使用擷取收件者弱式參考資料的 lambda 註冊事件處理常式。The code registers an event handler using a lambda that captures a weak reference to the recipient.

winrt::Windows::UI::Xaml::Controls::SwapChainPanel m_swapChainPanel;
winrt::event_token m_compositionScaleChangedEventToken;

void RegisterEventHandler()
{
    m_compositionScaleChangedEventToken = m_swapChainPanel.CompositionScaleChanged([weak_this{ get_weak() }]
        (Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
        Windows::Foundation::IInspectable const& object)
    {
        if (auto strong_this{ weak_this.get() })
        {
            strong_this->OnCompositionScaleChanged(sender, object);
        }
    });
}

void OnCompositionScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender,
    Windows::Foundation::IInspectable const& object)
{
    // Here, we know that the "this" object is valid.
}

Lamba 擷取子句中,已建立暫存變數,代表 this 的弱式參考。In the lamba capture clause, a temporary variable is created, representing a weak reference to this. 在 lambda 本文中,如果可以取得 this 的強式參考,則可呼叫 OnCompositionScaleChanged 函式。In the body of the lambda, if a strong reference to this can be obtained, then the OnCompositionScaleChanged function is called. 如此一來,在 OnCompositionScaleChanged 中,可安全使用 this 。That way, inside OnCompositionScaleChanged, this can safely be used.

C++/WinRT 中的弱式參考Weak references in C++/WinRT

在上述內容中,我們已看到正在使用的弱式參考。Above, we saw weak references being used. 一般情況下,此方式適合用來中斷循環參考。In general, they're good for breaking cyclic references. 例如,XAML 型 UI 架構的原生實作需要 C++/WinRT 中的弱式參考機制才能處理循環參考—因為架構的歷史設計—。For example, for the native implementation of the XAML-based UI framework—because of the historical design of the framework—the weak reference mechanism in C++/WinRT is necessary to handle cyclic references. 不過,除了 XAML,您可能不需要使用弱式參考 (請注意任何在本質上是 XAML 特有內容的項目)。Outside of XAML, though, you likely won't need to use weak references (not that there's anything inherently XAML-specific about them). 您應該多以這種方法來設計自己的 C++/WinRT API,以避免循環參考和弱式參考的需求。Rather you should, more often than not, be able to design your own C++/WinRT APIs in such a way as to avoid the need for cyclic references and weak references.

針對您宣告的任何指定類型,當弱式參考是必要的,是否無法馬上看得出來是 C++/WinRT。For any given type that you declare, it's not immediately obvious to C++/WinRT whether or when weak references are needed. 因此,C++/WinRT 自動在結構範本winrt::implements 上提供弱式參考支援,您的 C++/WinRT 類型直接或間接從其中衍生。So, C++/WinRT provides weak reference support automatically on the struct template winrt::implements, from which your own C++/WinRT types directly or indirectly derive. 它是使用付費,其中您不需要支付任何項目除非確實針對 IWeakReferenceSource 查詢您的物件。It's pay-for-play, in that it doesn't cost you anything unless your object is actually queried for IWeakReferenceSource. 且您可以明確選擇退出該支援And you can choose explicitly to opt out of that support.

程式碼範例Code examples

winrt::weak_ref結構範本是取得類別執行個體的弱式參考選項之一。The winrt::weak_ref struct template is one option for getting a weak reference to a class instance.

Class c;
winrt::weak_ref<Class> weak{ c };

或者,您可以使用 winrt::make_weak 協助程式函式。Or, you can use the use the winrt::make_weak helper function.

Class c;
auto weak = winrt::make_weak(c);

建立弱式參考並不會影響物件本身的參考計數。只會配置控制區。Creating a weak reference doesn't affect the reference count on the object itself; it just causes a control block to be allocated. 該控制區負責實作弱式參考語意。That control block takes care of implementing the weak reference semantics. 然後您可以嘗試將弱式參考升級為強式參考,如果成功了,請使用它。You can then try to promote the weak reference to a strong reference and, if successful, use it.

if (Class strong = weak.get())
{
    // use strong, for example strong.DoWork();
}

提供的一些其他強式參考仍然存在,weak_ref::get 呼叫遞增參考計數並將強式參考傳回給該呼叫者。Provided that some other strong reference still exists, the weak_ref::get call increments the reference count and returns the strong reference to the caller.

不使用弱式參考資料支援Opting out of weak reference support

弱式參考資料支援會自動執行。Weak reference support is automatic. 但您可以藉由傳遞 winrt::no_weak_ref 標記結構做為基底類別的範本引數,明確地選擇退出物件支援。But you can choose explicitly to opt out of that support by passing the winrt::no_weak_ref marker struct as a template argument to your base class.

如果您直接從 winrt::implements 衍生。If you derive directly from winrt::implements.

struct MyImplementation: implements<MyImplementation, IStringable, no_weak_ref>
{
    ...
}

如果您正在撰寫執行階段類別。If you're authoring a runtime class.

struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, no_weak_ref>
{
    ...
}

標記結構在 variadic 參數套件中出現的位置並不重要。It doesn't matter where in the variadic parameter pack the marker struct appears. 如果您要求弱式參考一個退出的類型,然後編譯器會協助您使用「這僅適用於弱式參考支援」。If you request a weak reference for an opted-out type, then the compiler will help you out with "This is only for weak ref support".

重要 APIImportant APIs