Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Important
Entwickeln mit dem Windows App SDK? Der Code dieses Artikels verwendet UWP(Windows.UI.Xaml)-Namespaces. Wenn Ihr Projekt auf WinUI 3 (Windows App SDK) abzielt, ersetzen Sie durchgängig Microsoft.UI.Xaml sowie die zugehörigen Microsoft.UI.*-Namespaces. Eine vollständige Zuordnung finden Sie unter Zuordnen von UWP-APIs dem Windows App SDK und weitere Details im Leitfaden zur UI-Migration.
Die Windows-Runtime ist ein System mit Referenzzählung; und in einem solchen System ist es wichtig, dass Sie die Bedeutung von starken und schwachen Referenzen sowie den Unterschied zwischen ihnen kennen (und auch von Referenzen, die weder das eine noch das andere sind, wie etwa dem impliziten this-Zeiger). Wie Sie in diesem Abschnitt sehen werden, kann das Wissen darüber, wie diese Verweise richtig zu verwalten sind, den Unterschied ausmachen zwischen einem zuverlässigen System, das reibungslos läuft, und einem System, das unvorhersehbar abstürzt. Durch die Bereitstellung von Hilfsfunktionen, die tief in die Sprachprojektion integriert sind, kommt Ihnen C++/WinRT beim einfachen und korrekten Erstellen komplexerer Systeme entgegen.
Note
Mit nur wenigen Ausnahmen ist die Unterstützung für schwache Verweise standardmäßig für Windows-Runtime Typen aktiviert, die Sie in C++/WinRT verwenden oder erstellen. Windows.UI.Composition und Windows.Devices.Input.PenDevice sind Beispiele für Ausnahmen, d. h. Namespaces, in denen die Unterstützung für schwache Verweise für diese Typen nicht aktiviert ist. Siehe auch Wenn Ihr automatisch widerrufener Delegierter nicht registriert werden kann.
Wenn Sie Typen erstellen, lesen Sie den Abschnitt Schwache Verweise in C++/WinRT in diesem Thema.
Sicherer Zugriff auf den this-Zeiger in einer Klassenmember-Koroutine
Weitere Informationen zu Coroutines und Codebeispielen finden Sie unter Concurrency und asynchrone Vorgänge mit C++/WinRT.
Die folgende Codeauflistung zeigt ein typisches Beispiel für eine Coroutine, die eine Memberfunktion einer Klasse ist. Sie können dieses Beispiel in die angegebenen Dateien in ein neues Windows Konsolenanwendungsprojekt (C++/WinRT) kopieren.
// 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 verbringt einige Zeit mit der Arbeit und gibt schließlich eine Kopie des MyClass::m_value Datenelements zurück. Der Aufruf von RetrieveValueAsync führt dazu, dass ein asynchrones Objekt erzeugt wird, und dieses Objekt verfügt über einen impliziten this-Zeiger, über den schließlich auf m_value zugegriffen wird.
Denken Sie daran, dass in einer Coroutine die Ausführung bis zum ersten Unterbrechungspunkt synchron abläuft, an dem die Kontrolle an den Aufrufer zurückgegeben wird. In RetrieveValueAsync ist das erste co_await der erste Unterbrechungspunkt. Wenn die Coroutine fortgesetzt wird (in diesem Fall etwa fünf Sekunden später), könnte mit dem impliziten this-Zeiger, über den wir auf m_value zugreifen, alles Mögliche passiert sein.
Dies ist die vollständige Abfolge von Ereignissen.
- Im Hauptteil wird eine Instanz von MyClass erstellt (
myclass_instance). - Das
async-Objekt wird erstellt und verweist (über sein this) aufmyclass_instance. - Die winrt::Windows::Foundation::IAsyncAction::get-Funktion erreicht ihren ersten Unterbrechungspunkt, blockiert für einige Sekunden und gibt dann das Ergebnis von RetrieveValueAsync zurück.
-
RetrieveValueAsync gibt den Wert von
this->m_value.
Schritt 4 ist nur dann sicher, wenn dies gültig bleibt.
Aber was geschieht, wenn die Klasseninstanz vor Abschluss des asynchronen Vorgangs zerstört wird? Es gibt viele Möglichkeiten, wie die Klasseninstanz aus dem Gültigkeitsbereich geraten kann, bevor die asynchrone Methode abgeschlossen ist. Aber wir können sie simulieren, indem wir die Klasseninstanz auf nullptr setzen.
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;
}
Nach dem Zeitpunkt, an dem wir die Klasseninstanz löschen, scheint es, als würden wir nicht mehr direkt auf sie verweisen. Das asynchrone Objekt weist jedoch natürlich einen solchen Zeiger darauf auf und versucht, ihn zum Kopieren des in der Klasseninstanz gespeicherten Werts zu verwenden. Die Coroutine ist eine Memberfunktion und geht davon aus, dass sie ihren this-Zeiger ohne Weiteres verwenden kann.
Mit dieser Änderung am Code treten in Schritt 4 ein Problem auf, da die Klasseninstanz zerstört wurde und dies nicht mehr gültig ist. Sobald das asynchrone Objekt versucht, auf die Variable innerhalb der Klasseninstanz zuzugreifen, stürzt es ab (oder führt etwas völlig undefiniert aus).
Die Lösung besteht darin, dem asynchronen Vorgang – der Coroutine – einen eigenen starken Verweis auf die Klasseninstanz zu geben. In der aktuellen Form hält die Coroutine effektiv einen rohen this-Zeiger auf die Klasseninstanz; doch das reicht nicht aus, um die Klasseninstanz am Leben zu erhalten.
Um die Klasseninstanz lebendig zu halten, ändern Sie die Implementierung von RetrieveValueAsync in die unten gezeigte.
IAsyncOperation<winrt::hstring> RetrieveValueAsync()
{
auto strong_this{ get_strong() }; // Keep *this* alive.
co_await 5s;
co_return m_value;
}
Eine C++/WinRT-Klasse wird direkt oder indirekt von der Winrt::implements-Vorlage abgeleitet. Aus diesem Grund kann das C++/WinRT-Objekt seine implements::get_strong-geschützte Memberfunktion aufrufen, um einen starken Verweis auf seinen this-Zeiger zu erhalten. Beachten Sie, dass die Variable strong_this im obigen Codebeispiel nicht tatsächlich verwendet werden muss; durch das einfache Aufrufen von get_strong wird die Referenzzählung des C++/WinRT-Objekts erhöht und sein impliziter this-Zeiger gültig gehalten.
Important
Da get_strong eine Memberfunktion der Winrt::implements-Strukturvorlage ist, können Sie sie nur von einer Klasse aufrufen, die direkt oder indirekt von winrt::implements abgeleitet ist, z. B. eine C++/WinRT-Klasse. Weitere Informationen zum Ableiten von winrt::implements und Beispielen finden Sie unter Autor-APIs mit C++/WinRT.
Dadurch wird das Problem behoben, das wir zuvor bei Schritt 4 hatten. Auch wenn alle anderen Verweise auf die Klasseninstanz verschwinden, hat die Coroutine Vorkehrungen getroffen, um zu garantieren, dass ihre Abhängigkeiten stabil sind.
Wenn ein starker Verweis nicht geeignet ist, können Sie stattdessen implements::get_weak aufrufen, um einen schwachen Verweis darauf abzurufen. Vergewissern Sie sich einfach, dass Sie einen starken Verweis abrufen können, bevor Sie darauf zugreifen. Auch hier ist get_weak eine Memberfunktion der Strukturvorlage winrt::implements .
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"";
}
}
Im obigen Beispiel wird durch den schwachen Verweis nicht verhindert, dass die Klasseninstanz zerstört wird, wenn keine starken Verweise verbleiben. Es bietet Ihnen jedoch eine Möglichkeit zu prüfen, ob ein starker Verweis bezogen werden kann, bevor auf die Mitgliedsvariable zugegriffen wird.
Sicherer Zugriff auf diesen Zeiger mit einem Ereignisbehandlungsdelegat
Das Szenario
Allgemeine Informationen zur Ereignisbehandlung finden Sie unter Behandeln von Ereignissen mithilfe von Delegaten in C++/WinRT.
Im vorherigen Abschnitt wurden potenzielle Lebenslebensdauerprobleme in den Bereichen Coroutinen und Parallelität hervorgehoben. Wenn Sie jedoch ein Ereignis mit der Memberfunktion eines Objekts oder innerhalb einer Lambda-Funktion innerhalb der Memberfunktion eines Objekts behandeln, müssen Sie über die relative Lebensdauer des Ereignisempfängers (das Objekt, das das Ereignis behandelt) und die Ereignisquelle (das Objekt, das das Ereignis auslöst) nachdenken. Sehen wir uns einige Codebeispiele an.
Die folgende Codeauflistung definiert zunächst eine einfache EventSource-Klasse, die ein generisches Ereignis auslöst, das von allen Delegaten behandelt wird, die ihm hinzugefügt wurden. Dieses Beispielereignis verwendet den delegattyp Windows::Foundation::EventHandler, aber die hier aufgeführten Probleme und Abhilfemaßnahmen gelten für alle Stellvertretungstypen.
Anschließend stellt die EventRecipient-Klasse einen Handler für das EventSource::Event-Ereignis in Form einer Lambda-Funktion bereit.
// 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();
}
Das Muster besteht darin, dass der Ereignisempfänger über einen Lambda-Ereignishandler mit Abhängigkeiten von diesem Zeiger verfügt. Wenn der Ereignisempfänger die Ereignisquelle überlebt, überlebt er auch diese Abhängigkeiten. Und in diesen Fällen, die häufig vorkommen, funktioniert das Muster gut. Einige dieser Fälle sind offensichtlich, z. B. wenn eine UI-Seite ein Ereignis behandelt, das von einem Steuerelement ausgelöst wird, das sich auf der Seite befindet. Die Seite überlebt die Schaltfläche. Der Handler überlebt also auch die Schaltfläche. Dies gilt immer dann, wenn dem Empfänger die Quelle gehört (zum Beispiel als Mitgliedsvariable) oder wenn der Empfänger und die Quelle Geschwisterobjekte sind und direkt zu einem anderen Objekt gehören.
Wenn Sie sicher sind, dass Sie einen Fall vor sich haben, in dem der Handler das this, von dem er abhängt, nicht überleben wird, dann können Sie this wie gewohnt erfassen, ohne Rücksicht auf eine starke oder schwache Lebensdauer.
Es gibt jedoch nach wie vor Fälle, in denen this seine Verwendung in einem Handler nicht überlebt (einschließlich Handlern für Abschluss- und Fortschrittsereignisse, die von asynchronen Aktionen und Vorgängen ausgelöst werden), und es ist wichtig zu wissen, wie man damit umgeht.
- Wenn eine Ereignisquelle ihre Ereignisse synchron auslöst, können Sie Ihren Handler abmelden und sicher sein, dass Sie keine weiteren Ereignisse mehr erhalten. Bei asynchronen Ereignissen kann ein In-Flight-Ereignis jedoch auch nach dem Widerrufen (und insbesondere beim Widerrufen innerhalb des Destruktors) ihr Objekt erreichen, nachdem es mit der Destruktierung begonnen hat. Eine Stelle zu finden, an der Sie sich vor der Zerstörung abmelden können, könnte das Problem mildern, aber lesen Sie weiter, um eine robuste Lösung zu erhalten.
- Wenn Sie eine Coroutine erstellen, um eine asynchrone Methode zu implementieren, ist dies möglich.
- In seltenen Fällen kann es bei bestimmten XAML-UI-Framework-Objekten (z. B. SwapChainPanel) vorkommen, wenn der Empfänger finalisiert wird, ohne sich von der Ereignisquelle abzumelden.
Das Problem
Diese nächste Version der Funktion main simuliert, was passiert, wenn der Ereignisempfänger zerstört wird (etwa wenn er den Gültigkeitsbereich verlässt), während die Ereignisquelle weiterhin Ereignisse auslöst.
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.
}
Der Ereignisempfänger wird zerstört, aber der darin enthaltene Lambda-Event-Handler ist weiterhin für das Event registriert. Wenn dieses Ereignis ausgelöst wird, versucht die Lambda-Funktion, den this-Zeiger zu dereferenzieren, der zu diesem Zeitpunkt ungültig ist. Eine Zugriffsverletzung ergibt sich also aus Code im Handler (oder in der Fortsetzung eines Coroutines), der versucht, ihn zu verwenden.
Important
Wenn Sie auf eine ähnliche Situation stoßen, müssen Sie über die Lebensdauer dieses Objekts nachdenken. und ob das erfasste Objekt die Aufnahme überlebt. Wenn das nicht der Fall ist, dann halten Sie es mit einer starken oder einer schwachen Referenz, wie wir unten zeigen.
Oder – wenn das für Ihr Szenario sinnvoll ist und wenn Fragen der Threadverarbeitung es überhaupt zulassen – gibt es noch die Möglichkeit, die Registrierung des Handlers aufzuheben, nachdem der Empfänger das Ereignis verarbeitet hat, oder im Destruktor des Empfängers. Siehe "Widerrufen einer registrierten Stellvertretung".
So registrieren wir den Handler.
event_source.Event([&](auto&& ...)
{
std::wcout << m_value.c_str() << std::endl;
});
Mit der Lambda-Funktion werden alle lokalen Variablen automatisch anhand eines Verweises erfasst. Für dieses Beispiel könnten wir dies also entsprechend geschrieben haben.
event_source.Event([this](auto&& ...)
{
std::wcout << m_value.c_str() << std::endl;
});
In beiden Fällen erfassen wir nur den unformatierten Zeiger. Und das hat keine Auswirkung auf die Verweiszählung, sodass nichts verhindert, dass das aktuelle Objekt zerstört wird.
Die Lösung
Die Lösung besteht darin, einen starken Verweis zu erfassen (oder, wie wir sehen, einen schwachen Verweis, wenn dies besser geeignet ist). Ein starker Verweis erhöht die Verweisanzahl und behält das aktuelle Objekt am Leben. Sie deklarieren einfach eine Capture-Variable (in diesem Beispiel strong_this genannt) und initialisieren sie mit einem Aufruf von implements::get_strong, wodurch ein starker Verweis auf unseren this-Zeiger abgerufen wird.
Important
Da get_strong eine Memberfunktion der Winrt::implements-Strukturvorlage ist, können Sie sie nur von einer Klasse aufrufen, die direkt oder indirekt von winrt::implements abgeleitet ist, z. B. eine C++/WinRT-Klasse. Weitere Informationen zum Ableiten von winrt::implements und Beispielen finden Sie unter Autor-APIs mit C++/WinRT.
event_source.Event([this, strong_this { get_strong()}](auto&& ...)
{
std::wcout << m_value.c_str() << std::endl;
});
Sie können sogar die automatische Capture des aktuellen Objekts weglassen und stattdessen auf den Datenmember über die Capture-Variable statt über das implizite this zugreifen.
event_source.Event([strong_this { get_strong()}](auto&& ...)
{
std::wcout << strong_this->m_value.c_str() << std::endl;
});
Wenn ein starker Verweis nicht geeignet ist, können Sie stattdessen implements::get_weak aufrufen, um einen schwachen Verweis darauf abzurufen. Ein schwacher Verweis behält das aktuelle Objekt nicht am Leben. Stellen Sie also einfach sicher, dass Sie weiterhin einen starken Verweis aus dem schwachen Verweis abrufen können, bevor Sie auf Mitglieder zugreifen.
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;
}
});
Wenn Sie einen unformatierten Zeiger erfassen, müssen Sie sicherstellen, dass das Zeigerobjekt lebendig bleibt.
Wenn Sie eine Memberfunktion als Stellvertretung verwenden
Neben Lambda-Funktionen gelten diese Prinzipien auch bei der Verwendung einer Mitgliedsfunktion als Delegat. Die Syntax ist anders. Sehen wir uns also einen Code an. Zunächst sehen Sie hier den potenziell unsicheren Ereignishandler einer Memberfunktion, der einen rohen this-Zeiger verwendet.
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;
}
};
Dies ist die herkömmliche Standardmethode, um auf ein Objekt und seine Memberfunktion zu verweisen. Um dies zu schützen, können Sie – ab Version 10.0.17763.0 (Windows 10, Version 1809) des Windows SDK – einen starken oder schwachen Verweis an dem Punkt einrichten, an dem der Handler registriert ist. An diesem Punkt ist bekannt, dass das Ereignisempfängerobjekt noch aktiv ist.
Für eine starke Referenz rufen Sie einfach get_strong anstelle des rohen this-Zeigers auf. C++/WinRT stellt sicher, dass der resultierende Delegat einen starken Verweis auf das aktuelle Objekt enthält.
event_source.Event({ get_strong(), &EventRecipient::OnEvent });
Das Erfassen eines starken Verweises bedeutet, dass Ihr Objekt erst nach dem Aufheben der Registrierung des Handlers zur Vernichtung berechtigt wird und alle ausstehenden Rückrufe zurückgegeben wurden. Diese Garantie ist jedoch nur gültig, wenn das Ereignis ausgelöst wird. Wenn Ihr Ereignishandler asynchron ist, müssen Sie Ihrer Koroutine vor dem ersten Suspendierungspunkt eine starke Referenz auf die Klasseninstanz geben (Einzelheiten und Code finden Sie weiter oben in diesem Thema im Abschnitt Sicherer Zugriff auf den this-Zeiger in einer Koroutine eines Klassenmembers). Dadurch wird jedoch ein Zirkelverweis zwischen der Ereignisquelle und dem Objekt erstellt, daher müssen Sie dies explizit unterbrechen, indem Sie das Ereignis widerrufen.
Rufen Sie für eine schwache Referenz get_weak auf. C++/WinRT stellt sicher, dass der resultierende Delegat einen schwachen Verweis enthält. Im letzten Moment und hinter den Kulissen versucht der Delegate, die schwache Referenz in eine starke umzuwandeln, und ruft die Member-Funktion nur auf, wenn dies erfolgreich ist.
event_source.Event({ get_weak(), &EventRecipient::OnEvent });
Wenn der Delegat Ihre Memberfunktion aufruft, hält C++/WinRT Ihr Objekt am Leben, bis Ihr Handler zurückkehrt. Wenn Ihr Handler jedoch asynchron ist, kehrt er an Aussetzungspunkten zurück, sodass Sie Ihrer Koroutine vor dem ersten Aussetzungspunkt eine starke Referenz auf die Klasseninstanz geben müssen. Auch hier finden Sie weitere Informationen weiter oben in diesem Thema im Abschnitt Sicherer Zugriff auf den this-Zeiger in einer Klassenmember-Koroutine.
Wenn die Memberfunktion nicht zu einem Windows-Runtime-Typ gehört
Wenn die get_strong-Methode für Sie nicht verfügbar ist (Ihr Typ ist kein Windows-Runtime Typ), können Sie die im folgenden Codebeispiel gezeigte Technik verwenden. Hier wird gezeigt, wie eine reguläre C++-Klasse (namens ConsoleNetworkWatcher) das NetworkInformation.NetworkStatusChanged-Ereignis verarbeitet.
#include <winrt/Windows.Networking.Connectivity.h>
using namespace winrt;
using namespace Windows::Networking::Connectivity;
class ConsoleNetworkWatcher
{
/* any constructor, and instance methods, here*/
static void Initialize(std::shared_ptr<ConsoleNetworkWatcher> instance)
{
auto weakPointer{ std::weak_ptr{ instance } };
instance->m_statusChangedRevoker =
NetworkInformation::NetworkStatusChanged(winrt::auto_revoke,
[weakPointer](winrt::Windows::Foundation::IInspectable const& sender)
{
auto sharedPointer{ weakPointer.lock() };
if (sharedPointer)
{
sharedPointer->NetworkStatusChanged(sender);
}
});
}
void NetworkStatusChanged(winrt::Windows::Foundation::IInspectable const& sender){/* handle event here */};
private:
NetworkInformation::NetworkStatusChanged_revoker m_statusChangedRevoker;
};
Ein schwaches Referenzbeispiel mit SwapChainPanel::CompositionScaleChanged
In diesem Codebeispiel verwenden wir das SwapChainPanel::CompositionScaleChanged-Ereignis mithilfe einer anderen Abbildung schwacher Verweise. Der Code registriert einen Ereignishandler mithilfe einer Lambda-Funktion, die einen schwachen Verweis auf den Empfänger erfasst.
winrt::Microsoft::UI::Xaml::Controls::SwapChainPanel m_swapChainPanel;
winrt::event_token m_compositionScaleChangedEventToken;
void RegisterEventHandler()
{
m_compositionScaleChangedEventToken = m_swapChainPanel.CompositionScaleChanged([weak_this{ get_weak() }]
(Microsoft::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(Microsoft::UI::Xaml::Controls::SwapChainPanel const& sender,
Windows::Foundation::IInspectable const& object)
{
// Here, we know that the "this" object is valid.
}
In der Lamba-Aufnahmeklausel wird eine temporäre Variable erstellt, die einen schwachen Verweis darauf darstellt. Wenn im Rumpf des Lambda-Ausdrucks eine starke Referenz auf this erlangt werden kann, wird die Funktion OnCompositionScaleChanged aufgerufen. Auf diese Weise kann dies in OnCompositionScaleChanged sicher verwendet werden.
Schwache Verweise in C++/WinRT
Oben haben wir gesehen, wie schwache Referenzen verwendet wurden. Im Allgemeinen eignen sie sich gut zum Aufbrechen zyklischer Referenzen. Beispielsweise ist für die systemeigene Implementierung des XAML-basierten UI-Frameworks – aufgrund des historischen Designs des Frameworks – der schwache Referenzmechanismus in C++/WinRT erforderlich, um zyklische Verweise zu behandeln. Außerhalb von XAML werden Sie allerdings wahrscheinlich keine schwachen Referenzen verwenden müssen (nicht, dass an ihnen etwas von Natur aus XAML-Spezifisches wäre). Vielmehr sollten Sie in den meisten Fällen in der Lage sein, Ihre eigenen C++/WinRT-APIs so zu entwerfen, dass zyklische und schwache Verweise gar nicht erst erforderlich sind.
Bei jedem Typ, den Sie deklarieren, ist für C++/WinRT nicht sofort ersichtlich, ob und gegebenenfalls wann schwache Referenzen erforderlich sind. C++/WinRT bietet also automatisch schwache Referenzunterstützung für die Strukturvorlage winrt::implements, von denen Ihre eigenen C++/WinRT-Typen direkt oder indirekt abgeleitet werden. Es ist pay-for-play, da sie nichts kostet, es sei denn, Ihr Objekt wird tatsächlich für IWeakReferenceSource abgefragt. Und Sie können sich explizit dafür entscheiden, diesen Support zu deaktivieren.
Code-Beispiele
Die Strukturvorlage "winrt::weak_ref " ist eine Option zum Abrufen eines schwachen Verweises auf eine Klasseninstanz.
Class c;
winrt::weak_ref<Class> weak{ c };
Oder Sie können auch die winrt::make_weak-Hilfsfunktion verwenden.
Class c;
auto weak = winrt::make_weak(c);
Das Erstellen eines schwachen Verweises wirkt sich nicht auf die Referenzanzahl des Objekts selbst aus. es bewirkt lediglich, dass ein Kontrollblock zugewiesen wird. Dieser Kontrollblock kümmert sich um die Implementierung der schwachen Referenzsemantik. Sie können dann versuchen, den schwachen Verweis in einen starken Verweis umzuwandeln und ihn, falls das erfolgreich ist, zu verwenden.
if (Class strong = weak.get())
{
// use strong, for example strong.DoWork();
}
Sofern noch ein anderer starker Verweis vorhanden ist, erhöht der weak_ref::get-Aufruf die Referenzanzahl und gibt den starken Verweis auf den Aufrufer zurück.
Abmelden von schwacher Referenzunterstützung
Die Unterstützung für schwache Referenzen erfolgt automatisch. Sie können diese Unterstützung jedoch explizit deaktivieren, indem Sie die winrt::no_weak_ref Markerstruktur als Vorlagenargument an Ihre Basisklasse übergeben.
Wenn Sie direkt von winrt::implements abgeleitet sind.
struct MyImplementation: implements<MyImplementation, IStringable, no_weak_ref>
{
...
}
Wenn Sie eine Laufzeitklasse erstellen.
struct MyRuntimeClass: MyRuntimeClassT<MyRuntimeClass, no_weak_ref>
{
...
}
Es spielt keine Rolle, wo im variadischen Parameterpaket die Markierungsstruktur angezeigt wird. Wenn Sie einen schwachen Verweis für einen Typ anfordern, der die Unterstützung schwacher Verweise deaktiviert hat, weist der Compiler Sie mit "Dies ist nur für die Unterstützung schwacher Verweise" darauf hin.
Wichtige APIs
Windows developer