Chytré ukazatele (moderní verze jazyka C++)

V moderním programování jazyka C++ obsahuje standardní knihovna inteligentní ukazatele, které slouží k zajištění toho, aby programy bez paměti a nevracení prostředků a byly bezpečné pro výjimky.

Použití inteligentních ukazatelů

Inteligentní ukazatele jsou definovány v std oboru názvů v< souboru hlavičky paměti>. Jsou zásadní pro inicializační programovací idiom raii nebo získávání prostředků. Hlavním cílem tohoto idiomu je zajistit, aby k pořízení prostředků docházelo v okamžiku inicializace objektu tak, aby všechny prostředky pro objekt byly vytvořeny a připraveny v jednom řádku kódu. Z praktického pohledu je hlavní zásadou idiomu RAII přiřadit vlastnictví libovolného prostředku přiděleného haldou – například dynamicky přidělené paměti nebo obslužným rutinám systémového objektu – objektu přidělenému zásobníkem, jehož destruktor obsahuje kód pro odstranění nebo uvolnění prostředku a také případný související čisticí kód.

Ve většině případů platí, že když inicializujete nezpracovaný ukazatel nebo obslužnou rutinu prostředku ukazující na skutečný prostředek, je třeba předat ukazatel inteligentnímu ukazateli okamžitě. V moderním jazyce C++ se nezpracované ukazatele používají pouze v malých blocích kódu s omezeným rozsahem, smyčkách nebo pomocných funkcích, kde je výkon velmi důležitý a neexistuje možnost záměny vlastnictví.

Následující příklad porovnává deklaraci nezpracovaného a inteligentního ukazatele.

void UseRawPointer()
{
    // Using a raw pointer -- not recommended.
    Song* pSong = new Song(L"Nothing on You", L"Bruno Mars"); 

    // Use pSong...

    // Don't forget to delete!
    delete pSong;   
}

void UseSmartPointer()
{
    // Declare a smart pointer on stack and pass it the raw pointer.
    unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));

    // Use song2...
    wstring s = song2->duration_;
    //...

} // song2 is deleted automatically here.

Jak je v příkladu vidět, inteligentní ukazatel je šablona třídy, která je deklarována v zásobníku a inicializována pomocí nezpracovaného ukazatele, který odkazuje na objekt přidělený haldou. Po inicializaci inteligentní ukazatel vlastní nezpracovaný ukazatel. To znamená, že inteligentní ukazatel je odpovědný za odstranění paměti, kterou nezpracovaný ukazatel určuje. Destruktor inteligentního ukazatele obsahuje volání pro odstranění. Protože inteligentní ukazatel je deklarován v zásobníku, jeho destruktor je vyvolán, když se inteligentní ukazatel dostane mimo rozsah, i když někde dále v zásobníku dojde k výjimce.

Přístup k zapouzdřenému ukazateli pomocí známých operátorů ukazatele a ->*, které třída inteligentního ukazatele přetíží, aby vrátil zapouzdřený nezpracovaný ukazatel.

Idiom inteligentního ukazatele v jazyce C++ se podobá vytvoření objektu v jazycích jako např. C#, kde vytvoříte objekt a necháte systém, aby se ve správný čas postaral o jeho odstranění. Rozdíl je v tom, že na pozadí neběží žádný samostatný systém uvolňování paměti. Paměť je spravována prostřednictvím standardních pravidel oboru C++, takže běhové prostředí je rychlejší a efektivnější.

Důležité

Vždy vytvářejte inteligentní ukazatele na samostatném řádku kódu, nikdy v seznamu parametrů, aby nedocházelo k mírnému úniku prostředků kvůli některým pravidlům přidělování seznamu parametrů.

Následující příklad ukazuje, jak lze unique_ptr k zapouzdření ukazatele na velký objekt použít inteligentní typ ukazatele ze standardní knihovny jazyka C++.


class LargeObject
{
public:
    void DoSomething(){}
};

void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{    
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Pass a reference to a method.
    ProcessLargeObject(*pLarge);

} //pLarge is deleted automatically when function block goes out of scope.

Příklad demonstruje následující základní kroky pro použití inteligentních ukazatelů.

  1. Deklarujte inteligentní ukazatel jako automatickou (místní) proměnnou. (Nepoužívejte new ani malloc výraz na samotném inteligentním ukazateli.)

  2. U parametru typu zadejte typ odkazování zapouzdřeného ukazatele.

  3. Předejte nezpracovaný newukazatel na objekt -ed v konstruktoru inteligentního ukazatele. (Některé funkce nástrojů nebo konstruktory inteligentního ukazatele to provedou za vás.)

  4. Pro přístup k objektu použijte přetížené -> operátory a * operátory.

  5. Umožněte inteligentnímu ukazateli odstranit objekt.

Inteligentní ukazatele jsou navrženy tak, aby byly co nejúčinnější z hlediska paměti i výkonu. Jediným datovým členem unique_ptr je například zapouzdřený ukazatel. To znamená, že unique_ptr je přesně stejná velikost jako tento ukazatel, a to buď čtyři bajty, nebo osm bajtů. Přístup k zapouzdřenému ukazateli pomocí přetíženého inteligentního ukazatele * a –> operátory nejsou výrazně pomalejší než přímý přístup k nezpracovaným ukazatelům.

Inteligentní ukazatele mají své vlastní členské funkce, ke kterým se přistupuje pomocí notace "tečka". Některé inteligentní ukazatele standardní knihovny C++ mají například funkci resetování člena, která uvolní vlastnictví ukazatele. To je užitečné, pokud chcete uvolnit paměť vlastněnou inteligentním ukazatelem předtím, než se inteligentní ukazatel dostane mimo rozsah platnosti, jak je znázorněno v následujícím příkladu.

void SmartPointerDemo2()
{
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Free the memory before we exit function block.
    pLarge.reset();

    // Do some other work...

}

Inteligentní ukazatele obvykle umožňují přímý přístup k příslušnému nezpracovanému ukazateli. Inteligentní ukazatele standardní knihovny C++ mají get pro tento účel členovou funkci a CComPtr mají člena veřejné p třídy. Díky poskytnutí přímého přístupu k podkladový ukazateli můžete použít inteligentní ukazatel ke správě paměti ve vlastním kódu, a zároveň předávat nezpracovaný ukazatel kódu, který nepodporuje inteligentní ukazatele.

void SmartPointerDemo4()
{
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Pass raw pointer to a legacy API
    LegacyLargeObjectFunction(pLarge.get());    
}

Druhy inteligentních ukazatelů

V následující části jsou shrnuty různé druhy inteligentních ukazatelů, které jsou k dispozici v programovacím prostředí Windows, včetně popisu, kdy je vhodné je použít.

Inteligentní ukazatele standardní knihovny C++

Tyto inteligentní ukazatele představují první volbu pro zapouzdření ukazatelů do objektů POCO.

  • unique_ptr
    Povolují právě jednoho vlastníka podkladového ukazatele. Použijte jako výchozí volbu pro POCO, pokud nevíte, že požadujete shared_ptr. Lze je přesunout na nového vlastníka, nikoli však kopírovat nebo sdílet. Nahrazuje auto_ptr, která je zastaralá. Porovnat s boost::scoped_ptr. unique_ptr je malá a účinná; velikost je jeden ukazatel a podporuje odkazy rvalue pro rychlé vkládání a načítání z kolekcí standardní knihovny C++. Hlavičkový soubor: <memory>. Další informace naleznete v tématu Postupy: Vytvoření a použití instancí unique_ptr a třídy unique_ptr.

  • shared_ptr
    Inteligentní ukazatel s počítáním referencí Tento typ je vhodný, když chcete přiřadit jeden nezpracovaný ukazatel více vlastníkům, například, když vrátíte kopii ukazatele z kontejneru, ale chcete zachovat originál. Nezpracovaný ukazatel se neodstraní, dokud všichni shared_ptr vlastníci neodešli rozsah nebo jinak neudělili vlastnictví. Má velikost dva ukazatele; jeden pro objekt a jeden pro sdílený kontrolní blok, který obsahuje počet odkazů. Hlavičkový soubor: <memory>. Další informace naleznete v tématu Postupy: Vytvoření a použití instancí shared_ptr a třídy shared_ptr.

  • weak_ptr
    Speciální inteligentní ukazatel pro použití ve spojení s shared_ptr. Poskytuje weak_ptr přístup k objektu, který vlastní jedna nebo více shared_ptr instancí, ale neúčastní se počítání odkazů. Použijte ho, chcete-li objekt sledovat, ale nepotřebujete ho ponechat ve stavu alive. Vyžaduje se v některých případech k přerušení cyklických odkazů mezi shared_ptr instancemi. Hlavičkový soubor: <memory>. Další informace najdete v tématu Postupy: Vytvoření a použití instancí weak_ptr a třídy weak_ptr.

Inteligentní ukazatele pro objekty MODELU COM (klasické programování systému Windows)

Při práci s objekty COM zabalte ukazatele rozhraní do příslušného typu inteligentního ukazatele. Knihovna ATL definuje řadu inteligentních ukazatelů pro různé účely. Můžete také použít typ inteligentního _com_ptr_t ukazatele, který kompilátor používá při vytváření tříd obálky ze souborů .tlb. Jedná se o nejlepší volbu, pokud nechcete zahrnovat hlavičkové soubory ATL.

CComPtr – třída
Tuto volbu použijte, dokud nenastane situace, že nemůžete použít knihovnu ATL. Provádí počítání odkazů pomocí AddRef metod a Release metod. Další informace naleznete v tématu Postupy: Vytvoření a použití instancí CComPtr a CComQIPtr.

CComQIPtr – třída
CComPtr Podobá se, ale také poskytuje zjednodušenou syntaxi pro volání QueryInterface objektů MODELU COM. Další informace naleznete v tématu Postupy: Vytvoření a použití instancí CComPtr a CComQIPtr.

CComHeapPtr – třída
Inteligentní ukazatel na objekty, které používají CoTaskMemFree k uvolnění paměti.

CComGITPtr – třída
Inteligentní ukazatel rozhraní, která se získávají z tabulky globálního rozhraní (GIT).

_com_ptr_t – třída
CComQIPtr Podobá se funkcím, ale nezávisí na hlavičkách ATL.

Inteligentní ukazatele ATL pro objekty POCO

Kromě inteligentních ukazatelů pro objekty COM definuje ATL také inteligentní ukazatele a kolekce inteligentních ukazatelů pro prosté staré objekty C++ (POCO). V klasickém programování systému Windows jsou tyto typy užitečné alternativy kolekcí standardní knihovny C++, zejména pokud není vyžadována přenositelnost kódu nebo pokud nechcete kombinovat programovací modely standardní knihovny C++ a knihovny ATL.

CAutoPtr – třída
Inteligentní ukazatel, který vynucuje jedinečné vlastnictví převodem vlastnictví na kopii. Srovnatelné s zastaralou std::auto_ptr třídou.

CHeapPtr – třída
Inteligentní ukazatel pro objekty, které jsou přiděleny pomocí funkce C malloc .

CAutoVectorPtr – třída
Inteligentní ukazatel pro pole, která jsou přidělena pomocí new[].

CAutoPtrArray – třída
Třída zapouzdřuje pole CAutoPtr prvků.

CAutoPtrList – třída
Třída zapouzdřuje metody pro manipulaci se seznamem CAutoPtr uzlů.

Viz také

Ukazatele
Referenční dokumentace jazyka C++
Standardní knihovna C++