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. 当协同例程恢复时(在此示例中为大约五秒以后),我们用来访问 m_value 的隐式 this 指针可能会发生任何情况。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. main 中创建 MyClass 的实例 (myclass_instance)。In main, an instance of MyClass is created (myclass_instance).
  2. 创建指向(通过 thismyclass_instanceasync 对象。The 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 指针,而此时该指针是无效的。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::CompositionScaleChanged 的弱引用示例A 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 中便可以安全地使用 thisThat 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