Sdílet prostřednictvím


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

V moderním programování v jazyce C++ standardní knihovna obsahuje inteligentní ukazatele, které se používají k zajištění toho, aby v programech nedocházelo k nedostatku paměti a dalších prostředků a byly bezpečné vůči výjimkám.

Použití inteligentních ukazatelů

Inteligentní ukazatele se definují v oboru názvů std v hlavičkovém souboru <memory>.Jsou klíčové pro programovací idiom RAII neboli Resource Acquisition Is Initialialization.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.

K zapouzdřenému ukazateli přejdete pomocí známých operátorů ukazatele, -> a *, které třída inteligentních ukazatelů přetíží a vrátí 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á poznámkaDů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 typ inteligentního ukazatele unique_ptr v knihovně Standard Template Library použít k zapouzdření ukazatele ve velkém objektu.

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 výraz new nebo malloc na samotném inteligentním ukazateli.)

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

  3. Předejte nezpracovaný ukazatel objektu new 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 *.

  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.Například jediný datový člen v unique_ptr je zapouzdřený ukazatel.To znamená, že unique_ptr má přesně stejnou velikost jako tento ukazatel – čtyři nebo osm bajtů.Přístup k zapouzdřenému ukazateli pomocí operátorů * a -> přetíženého inteligentního ukazatele není výrazně pomalejší než přímý přístup k nezpracovaným ukazatelům.

Inteligentní ukazatele mají své vlastní členské funkce, které jsou přístupné pomocí zápisu „tečka“.Některé inteligentní ukazatele STL například mají funkci obnovení člene, 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 STL pro tento účel disponují členskou funkcí get a CComPtr mají veřejného člena třídy p.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());    
}

Typy 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 knihovny C++ Standard
    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 je jako výchozí volbu pro objekty POCO, pokud s jistotou nevíte, že potřebujete ukazatele shared_ptr.Lze je přesunout na nového vlastníka, nikoli však kopírovat nebo sdílet.Nahrazují zastaralý ukazatel auto_ptr.Porovnejte je s ukazateli boost::scoped_ptr.Ukazatel unique_ptr je malý a efektivní. Má velikost jeden ukazatel a podporuje odkazy rvalue pro rychlé vkládání a načítání z kolekcí STL.Hlavičkový soubor: <memory>.Další informace naleznete v tématu Postupy: Vytváření a používání instancí ukazatelů unique_ptr a unique_ptr – třída.

    • 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 není odstraněn, dokud všichni vlastníci ukazatele shared_ptr nepřejdou mimo rozsah nebo se jinak nevzdají 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: Vytváření a používání instancí ukazatelů shared_ptr a shared_ptr – třída.

    • weak_ptr
      Inteligentní ukazatel pro zvláštní případy se používá ve spojení s shared_ptr.Ukazatel weak_ptr poskytuje přístup k objektu, který je vlastněn jednou nebo více instancemi ukazatele shared_ptr, ale sám se neúčastní počítání odkazů.Použijte ho, chcete-li objekt sledovat, ale nepotřebujete ho ponechat ve stavu alive.V některých případech je nutný pro přerušení cyklických odkazů mezi instancemi shared_ptr.Hlavičkový soubor: <memory>.Další informace naleznete v tématu Postupy: Vytváření a používání instancí ukazatelů weak_ptr a weak_ptr – třída.

  • Inteligentní ukazatele pro objekty COM (klasické programování v 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 použít také typ inteligentního ukazatele _com_ptr_t, který kompilátor používá při vytváření obálkových tříd ze souborů TLB.Jedná se o nejlepší volbu, pokud nechcete zahrnovat hlavičkové soubory 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 objekty POCO.V klasickém programování v systému Windows představují tyto typy užitečné alternativy ke kolekcím STL, zvláště pokud není požadována přenositelnost kódu nebo pokud nechcete kombinovat modely programování STL a ATL.

    • Třída CAutoPtr
      Inteligentní ukazatel, který vynucuje jedinečné vlastnictví převodem vlastnictví na kopii.Je srovnatelný se zastaralou třídou std::auto_ptr.

    • Třída CHeapPtr
      Inteligentní ukazatel pro objekty, které jsou přiřazovány pomocí funkce C malloc.

    • Třída CAutoVectorPtr
      Inteligentní ukazatel pro pole, která jsou přiřazována pomocí klíčového slova new[].

    • Třída CAutoPtrArray
      Třída, která zapouzdřuje pole prvků CAutoPtr.

    • Třída CAutoPtrList
      Třída, která zapouzdřuje metody pro práci se seznamem uzlů CAutoPtr.

Viz také

Další zdroje

C++ vás vítá zpět (moderní verze jazyka C++)

Referenční dokumentace jazyka C++

Standardní knihovna C++ – referenční dokumentace

Přehled: Správa paměti v jazyce C++