Pravidla pro správu referenčních počtů

Tip

Pomocí inteligentních ukazatelů můžete automatizovat AddRef/Release. Ruční počítání odkazů je náchylné k chybám. Moderní kód modelu COM jazyka C++ by měl používat inteligentní ukazatele, které volají AddRef a Release automaticky:

Inteligentní ukazatel Library Kdy ho použít
Microsoft::WRL::ComPtr<T> Knihovna šablon C++ prostředí prostředí Windows Runtime (WRL) WinRT a klasický model COM v jazyce C++
winrt::com_ptr<T> C++/WinRT Projekty C++/WinRT (upřednostňované pro nový kód)
wil::com_ptr<T> knihovny implementace Windows Jakýkoli kód modelu COM C++; přidá protokolování a diagnostiku.
ATL::CComPtr<T> knihovna ATL Starší projekty ATL (vyhněte se novému kódu)
// ✅ Smart pointer — Release is called automatically at end of scope
winrt::com_ptr<IStream> stream;
HRESULT hr = CreateStreamOnHGlobal(nullptr, TRUE, stream.put());

// ❌ Raw pointer — easy to leak on early return or exception
IStream* pStream = nullptr;
hr = CreateStreamOnHGlobal(nullptr, TRUE, &pStream);
// ... if any code path forgets pStream->Release(), you have a leak

Níže uvedená ruční pravidla platí i v případě, že pracujete s nezpracovanými IUnknown* ukazateli, starším kódem jazyka C nebo implementujete objekty MODELU COM interně.

Použití počtu odkazů ke správě životnosti objektu umožňuje více klientům získat a uvolnit přístup k jednomu objektu, aniž by se museli vzájemně koordinovat při správě životnosti objektu. Pokud objekt klienta odpovídá určitým pravidlům použití, poskytuje tento objekt tuto správu. Tato pravidla určují, jak spravovat odkazy mezi objekty. (COM neurčuje interní implementace objektů, přestože tato pravidla jsou rozumným výchozím bodem pro zásadu v rámci objektu.)

Koncepčně lze ukazatele rozhraní považovat za umístěné v proměnných ukazatele, které zahrnují veškerý interní výpočetní stav, který obsahuje ukazatel rozhraní. To by zahrnovalo proměnné v umístěních paměti, v registrech interních procesorů a proměnné generované programátorem a vygenerované kompilátorem. Přiřazení nebo inicializace proměnné ukazatele zahrnuje vytvoření nové kopie již existujícího ukazatele. Tam, kde byla v některé proměnné jedna kopie ukazatele (hodnota použitá v přiřazení nebo inicializaci), jsou teď dvě. Přiřazení proměnné ukazatele zničí kopii ukazatele v současné době v proměnné, stejně jako zničení samotné proměnné. (To znamená, že obor, ve kterém se nachází proměnná, například rámec zásobníku, je zničen.)

Z hlediska klienta modelu COM se počítání odkazů vždy provádí pro každé rozhraní. Klienti by nikdy neměli předpokládat, že objekt používá stejný čítač pro všechna rozhraní.

Výchozím případem je, že AddRef musí být volána pro každou novou kopii ukazatele rozhraní a release musí být volána pro každé zničení ukazatele rozhraní, s výjimkou případů, kdy následující pravidla povolují jinak:

  • In-out parametry funkcí. Volající musí volat AddRef parametru, protože se uvolní (s voláním Release) v implementovacím kódu, když je výstupní hodnota uložena nad ním.
  • Načtení globální proměnné Při vytváření místní kopie ukazatele rozhraní z existující kopie ukazatele v globální proměnné je nutné volat AddRef v místní kopii, protože jiná funkce může zničit kopii v globální proměnné, zatímco místní kopie je stále platná.
  • Nové ukazatele syntetizovány z "tenkého vzduchu". Funkce, která syntetizuje ukazatel rozhraní pomocí speciálních interních znalostí, a nikoli získat ho z jiného zdroje, musí volat AddRef zpočátku na nově syntetizovaném ukazateli. Mezi důležité příklady takových rutin patří rutiny vytváření instancí, implementace QueryInterfaceatd.
  • Načítání kopie interně uloženého ukazatele Když funkce načte kopii ukazatele, který je uložen interně objektem volaného, musí kód tohoto objektu volat AddRef na ukazateli před vrácením funkce. Po načtení ukazatele nemá původní objekt žádný jiný způsob, jak určit, jak jeho životnost souvisí s interní uloženou kopií ukazatele.

Jediné výjimky z výchozího případu vyžadují, aby správa kódu znala relace životnosti dvou nebo více kopií ukazatele na stejné rozhraní objektu a jednoduše se ujistila, že objekt není zničen tím, že jeho počet odkazů přejde na nulu. Obecně platí, že existují dva případy:

  • Pokud již existuje jedna kopie ukazatele a následně se vytvoří druhá a pak je zničena, zatímco první kopie stále existuje, lze vynechat volání AddRef a release pro druhou kopii.
  • Pokud existuje jedna kopie ukazatele a vytvoří se druhá a první je zničen před druhou, volání AddRef druhé kopie a release pro první kopii lze vynechat.

Tady jsou konkrétní příklady těchto situací, přičemž první dva jsou zvlášť běžné:

  • Parametry pro funkce. Doba života kopie ukazatele rozhraní předaného jako parametr funkce je vnořena do ukazatele použitého k inicializaci hodnoty, takže není nutné, aby byl u parametru nutný samostatný počet odkazů.
  • Out parameters from functions, including return values. Aby funkce nastavil parametr, musí mít stabilní kopii ukazatele rozhraní. Při návratu je volající zodpovědný za uvolnění ukazatele. Parametr out proto nepotřebuje samostatný počet odkazů.
  • Místní proměnné. Implementace metody má kontrolu nad životností jednotlivých proměnných ukazatelů přidělených v rámci zásobníku a může ji použít k určení, jak vynechat redundantní AddRef/release páry.
  • Backpointery. Některé datové struktury obsahují dva objekty, z nichž každý má ukazatel na druhý. Pokud je životnost prvního objektu známa jako životnost druhého objektu, není nutné mít referenční počet na ukazatel druhého objektu na první objekt. Tento cyklus je často důležitý při udržování vhodného chování přidělení. Nevěrné ukazatele by však měly být použity s extrémní opatrností, protože část operačního systému, která zpracovává vzdálené zpracování, nemá žádný způsob, jak o tomto vztahu vědět. Proto je upřednostňovaným řešením téměř ve všech případech, kdy backpointer vidí druhý "přátelský" objekt prvního ukazatele (čímž se vyhnete cykličnosti). Architektura připojitelných objektů modelu COM například používá tento přístup.

Při implementaci nebo použití objektů počítaných s odkazem může být užitečné použít počet umělých odkazů, které zaručují stabilitu objektů při zpracování funkce. Při implementaci metody rozhraní můžete volat funkce, které mají šanci zvýšit počet odkazů na objekt, což způsobuje předčasné uvolnění objektu a selhání implementace. Robustní způsob, jak tomu zabránit, je vložit volání AddRef na začátku implementace metody a spárovat ji s voláním Release těsně před vrácením metody.

V některých situacích můžou být návratové hodnoty AddRef a release nestabilní a nemělo by se spoléhat na; měly by se používat pouze pro účely ladění nebo diagnostiky.

správa životností objektů prostřednictvím počítání odkazů