it-swarm-eu.dev

Co je chytrý ukazatel a kdy ho mám použít?

Co je chytrý ukazatel a kdy ho mám použít?

1616
Alex Reynolds

Inteligentní ukazatel je třída, která zalomí ukazatel „raw“ (nebo „holý“) C++, aby se tak řídila životnost objektu, na který odkazuje. Neexistuje žádný jediný typ inteligentního ukazatele, ale všechny z nich se snaží praktickým způsobem analyzovat surový ukazatel.

Inteligentní ukazatele by měly být preferovány před syrovými ukazateli. Pokud máte pocit, že potřebujete použít ukazatele (nejprve zvážit, zda jste opravdu do), normálně byste chtěli použít inteligentní ukazatel, protože to může zmírnit mnoho problémů se surovými ukazateli, hlavně zapomenout na odstranění objektu a úniku paměti.

Se surovými ukazateli musí programátor explicitně zničit objekt, když již není užitečný.

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject(); 
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

Inteligentní ukazatel porovnáním definuje zásadu, kdy je objekt zničen. Stále musíte vytvořit objekt, ale už se nemusíte starat o jeho zničení.

SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending 
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething() 
// raises an exception

Nejjednodušší používaná politika zahrnuje rozsah objektu inteligentního ukazatele wrapper, například implementovaného boost::scoped_ptr nebo std::unique_ptr .

void f()
{
    {
       std::unique_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}

std::unique_ptr instance nelze zkopírovat. Tím zabráníte, aby byl ukazatel několikrát odstraněn (nesprávně). Můžete však předávat odkazy na jiné funkce, které voláte.

std::unique_ptrs jsou užitečné, pokud chcete svázat životnost objektu s určitým blokem kódu, nebo pokud jej vložíte jako data člena do jiného objektu, doživotnost tohoto jiného objektu. Objekt existuje, dokud není ukončen blok obsahující kód, nebo dokud není samotný objekt zničen.

Komplexnější zásady inteligentního ukazatele zahrnují odkaz počítání ukazatele. To umožní kopírování ukazatele. Když je poslední "odkaz" na objekt zničen, objekt je odstraněn. Tato politika je implementována pomocí boost::shared_ptr a std::shared_ptr .

void f()
{
    typedef std::shared_ptr<MyObject> MyObjectPtr; // Nice short alias
    MyObjectPtr p1; // Empty

    {
        MyObjectPtr p2(new MyObject());
        // There is now one "reference" to the created object
        p1 = p2; // Copy the pointer.
        // There are now two references to the object.
    } // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero. 
  // The object is deleted.

Odkazy počítané odkazy jsou velmi užitečné, když je životnost vašeho objektu mnohem složitější a není vázána přímo na určitou část kódu nebo na jiný objekt.

Je zde jedna nevýhoda odkazovat počítané ukazatele - možnost vytvoření visícího odkazu:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

Další možností je vytvoření kruhových odkazů:

struct Owner {
   std::shared_ptr<Owner> other;
};

std::shared_ptr<Owner> p1 (new Owner());
std::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

Chcete-li tento problém vyřešit, Boost i C++ 11 definovali weak_ptr definovat slabý (nespočetovaný) odkaz na shared_ptr.


UPDATE

Tato odpověď je poměrně stará, a tak popisuje, co bylo v té době „dobré“, což byly inteligentní ukazatele poskytované knihovnou Boost. Protože C++ 11, standardní knihovna poskytuje dostatečné typy inteligentních ukazatelů, a proto byste měli upřednostňovat použití std::unique_ptr , std::shared_ptr a std::weak_ptr .

Tam je také std::auto_ptr . Je to velmi podobné tomu, že má poskakovaný ukazatel, kromě toho, že má také "speciální" nebezpečnou schopnost kopírovat - což také nečekaně převádí vlastnictví! Je zastaralý v nejnovějších standardech, takže byste jej neměli používat. Místo toho použijte std::unique_ptr .

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership. 
                                 // p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.
1780
Lloyd

Zde je jednoduchá odpověď pro tyto dny moderního C++:

  • Co je chytrý ukazatel?
    Je to typ, jehož hodnoty lze použít jako ukazatele, ale který poskytuje další funkci automatické správy paměti: Když se inteligentní ukazatel již nepoužívá, paměť, na kterou odkazuje, je uvolněna (viz také podrobnější definice na Wikipedii ).
  • Kdy mám použít?
    V kódu, který zahrnuje sledování vlastnictví části paměti, přidělení nebo delokování; inteligentní ukazatel vám často ušetří potřebu dělat tyto věci explicitně.
  • Který inteligentní ukazatel bych měl použít ve kterých případech?
    • Použijte std::unique_ptr , pokud nechcete držet více odkazů na stejný objekt. Použijte jej například pro ukazatel na paměť, která bude přidělena při zadávání určitého rozsahu a při přidělování rozsahu bude zrušena.
    • Použijte std::shared_ptr , když chcete odkazovat na objekt z více míst - a nechcete, aby byl váš objekt de-přidělen, dokud všechny tyto odkazy nejsou pryč.
    • Použijte std::weak_ptr , když chcete odkazovat na svůj objekt z více míst - pro ty odkazy, pro které je v pořádku ignorovat a rozdělit (takže si jen všimnou, že objekt je pryč, když se snažíte dereference).
    • Nepoužívejte inteligentní ukazatele boost:: nebo std::auto_ptr kromě speciálních případů, které můžete přečíst, pokud musíte.
  • Hele, neptal jsem se, který z nich mám použít!
    Ah, ale opravdu jste to chtěli, přiznejte.
  • Tak kdy tedy mám používat pravidelné ukazatele?
    Většinou v kódu, který nevšímá vlastnictví paměti. To by obvykle bylo ve funkcích, které dostanou ukazatel z jiného místa a nepřidělují se, ani nepřidělují, a neukládají kopii ukazatele, který překonává jejich provedení.
231
einpoklum

Inteligentní ukazatel je ukazatelem typu s některými dalšími funkcemi, např. automatické ukládání paměti, počítání referencí atd.

Malé intro je k dispozici na stránce Inteligentní ukazatele - Co, proč, který? .

Jedním z jednoduchých typů smart-pointerů je std::auto_ptr (kapitola 20.4.5 standardu C++), která umožňuje automaticky uvolnit paměť, když je mimo rozsah a která je robustnější než použití jednoduchého ukazatele, když jsou výjimky hozeny. méně flexibilní.

Dalším praktickým typem je boost::shared_ptr který implementuje počítání referencí a automaticky uvolňuje paměť, když nezůstanou žádné odkazy na objekt. To pomáhá vyhnout se nevracení paměti a snadno se používá k implementaci RAII .

Předmět je podrobně popsán v knize "C++ Templates: The Complete Guide" od Davida Vandevoorde, Nicolai M. Josuttis , kapitola Kapitola 20. Inteligentní ukazatele. Některá témata:

104
sergtk

Definice poskytnuté Chrisem, Sergdevem a Llyodem jsou správné. Dávám přednost jednodušší definici ačkoli, jen abych udržel svůj život jednoduchý: Inteligentní ukazatel je prostě třída, která přetíží operátory -> a *. Což znamená, že váš objekt sémanticky vypadá jako ukazatel, ale můžete to udělat tak, aby chladnější věci, včetně počítání referencí, automatického zničení atd. shared_ptr a auto_ptr, byly ve většině případů dostačující, ale spolu s vlastní sadou malých idiosynkrází.

38
Sridhar Iyer

Inteligentní ukazatel je jako běžný (psaný) ukazatel, jako je znak „char *“, s výjimkou případů, kdy ukazatel sám zmizí mimo rozsah, na který odkazuje. Můžete jej použít jako běžný ukazatel pomocí "->", ale ne pokud potřebujete aktuální ukazatel k datům. K tomu můžete použít "& * ptr".

Je vhodný pro:

  • Objekty, které musí být přiděleny novým, ale které chcete mít stejný život jako něco v tomto zásobníku. Pokud je objekt přiřazen inteligentnímu ukazateli, budou při ukončení funkce/bloku odstraněny.

  • Data členové tříd, takže když je objekt smazán, všechna vlastní data jsou také vymazána, bez jakéhokoliv speciálního kódu v destruktoru (budete si muset být jisti, že destruktor je virtuální, což je téměř vždy dobrá věc) .

Můžete not chcete použít inteligentní ukazatel, když:

  • ... ukazatel by vlastně neměl vlastnit data ... tj. když právě používáte data, ale chcete, aby přežila funkci, na kterou odkazujete.
  • ... chytrý ukazatel není v žádném okamžiku zničen. Nechcete, aby seděla v paměti, která se nikdy nezničí (například v objektu, který je dynamicky přidělen, ale nebude explicitně smazán).
  • ... dva inteligentní ukazatele mohou ukazovat na stejná data. (Existují však i chytřejší ukazatele, které budou zpracovávat ..., které se nazývá počítání odkazů .

Viz také:

28
markets

Většina druhů inteligentních ukazatelů zachází s likvidací ukazatele na objekt. Je to velmi užitečné, protože už nemusíte přemýšlet o likvidaci objektů ručně.

Nejčastěji používané inteligentní ukazatele jsou std::tr1::shared_ptr (nebo boost::shared_ptr) a méně často std::auto_ptr. Doporučuji pravidelné používání shared_ptr.

shared_ptr je velmi všestranný a zabývá se širokou škálou scénářů likvidace, včetně případů, kdy je třeba objekty "projít přes hranice DLL" (běžný případ noční můry, pokud se mezi kódem a knihovnami DLL používají různé libcs) .

16

Inteligentní ukazatel je objekt, který se chová jako ukazatel, ale navíc poskytuje kontrolu nad konstrukcí, zničením, kopírováním, přesunem a dereferencí.

Jeden může realizovat něčí inteligentní ukazatel, ale mnoho knihoven také poskytuje inteligentní implementace ukazatele každý s různými výhodami a nevýhodami.

Například Boost poskytuje následující implementace inteligentního ukazatele:

  • shared_ptr<T> je ukazatel na T pomocí počtu referencí k určení, kdy objekt již není potřeba.
  • scoped_ptr<T> je ukazatel, který se automaticky vymaže, když je mimo rozsah. Žádné přiřazení není možné.
  • intrusive_ptr<T> je další ukazatel počítání referencí. Poskytuje lepší výkon než shared_ptr, ale vyžaduje, aby typ T poskytoval vlastní referenční mechanismus počítání.
  • weak_ptr<T> je slabý ukazatel, který pracuje ve spojení s shared_ptr, aby se zabránilo kruhovým odkazům.
  • shared_array<T> je jako shared_ptr, ale pro pole T.
  • scoped_array<T> je jako scoped_ptr, ale pro pole T.

Jedná se pouze o jeden lineární popis každého z nich, který lze použít podle potřeby, pro další podrobnosti a příklady se můžete podívat na dokumentaci Boost.

Standardní knihovna C++ navíc obsahuje tři inteligentní ukazatele; std::unique_ptr pro jedinečné vlastnictví, std::shared_ptr pro sdílené vlastnictví a std::weak_ptr. std::auto_ptr existoval v C++ 03, ale nyní je zastaralý.

16
Saqlain

Zde je Odkaz na podobné odpovědi: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

Inteligentní ukazatel je objekt, který působí, vypadá a cítí se jako normální ukazatel, ale nabízí více funkcí. V jazyce C++ jsou inteligentní ukazatele implementovány jako třídy šablon, které zapouzdřují ukazatel a přepisují standardní operátory ukazatelů. Mají řadu výhod oproti běžným ukazatelům. Je zaručeno, že budou inicializovány jako ukazatele null nebo ukazatele na objekt haldy. Je zkontrolován směr přes nulový ukazatel. Není nutné mazat. Objekty jsou automaticky uvolněny, když poslední ukazatel na ně zmizel. Jedním z významných problémů těchto inteligentních ukazatelů je, že na rozdíl od běžných ukazatelů nerespektují dědičnost. Inteligentní ukazatele jsou pro polymorfní kód neatraktivní. Níže je uveden příklad implementace inteligentních ukazatelů.

Příklad:

template <class X>
class smart_pointer
{
          public:
               smart_pointer();                          // makes a null pointer
               smart_pointer(const X& x)            // makes pointer to copy of x

               X& operator *( );
               const X& operator*( ) const;
               X* operator->() const;

               smart_pointer(const smart_pointer <X> &);
               const smart_pointer <X> & operator =(const smart_pointer<X>&);
               ~smart_pointer();
          private:
               //...
};

Tato třída implementovat inteligentní ukazatel na objekt typu X. Objekt samotný je umístěn na haldy. Zde je návod, jak jej používat:

smart_pointer <employee> p= employee("Harris",1333);

Stejně jako ostatní přetížení operátoři se p bude chovat jako běžný ukazatel,

cout<<*p;
p->raise_salary(0.5);
10
Santosh

http://en.wikipedia.org/wiki/Smart_pointer

V počítačové vědě je inteligentní ukazatel abstraktním typem dat, který simuluje ukazatel a zároveň poskytuje další funkce, jako je například automatická sbírka odpadu nebo kontrola hranic. Tyto další funkce mají omezit chyby způsobené zneužitím ukazatelů při zachování efektivity. Inteligentní ukazatele obvykle sledují objekty, které na ně odkazují pro účely správy paměti. Zneužití ukazatelů je hlavním zdrojem chyb: konstantní alokace, deallocation a odkazování, které musí být provedeny pomocí programu napsaného pomocí ukazatelů, způsobují, že dojde k určitému úniku paměti. Inteligentní ukazatele se snaží zabránit nevracení paměti tím, že automatickou distribuci prostředků uvolní prostředek: když je ukazatel na objekt (nebo poslední v sérii ukazatelů) zničen, například proto, že jde mimo rozsah, je také špičatý objekt zničen.

8
Jorge Ferreira

Nechť T je třída v tomto tutoriálu Ukazatele v C++ lze rozdělit do 3 typů:

1) Surové ukazatele :

T a;  
T * _ptr = &a; 

Oni drží adresu paměti k umístění v paměti. Používejte s opatrností, protože programy se stávají složitými na sledování.

Ukazatele s daty const nebo adresou {Číst zpět}

T a ; 
const T * ptr1 = &a ; 
T const * ptr1 = &a ;

Ukazatel na datový typ T, který je konst. Nemůžete změnit typ dat pomocí ukazatele. *ptr1 = 19; to nebude fungovat. Ale můžete přesunout ukazatel. ptr1++ , ptr1--; bude fungovat. Čtení dozadu: ukazatel na typ T, který je konst

  T * const ptr2 ;

Ukazatel const na datový typ T. Nemůžete přesunout ukazatel, ale můžete změnit hodnotu, na kterou ukazuje ukazatel. *ptr2 = 19 bude fungovat, ale ptr2++ ; ptr2-- atd. nebude fungovat. Čtení zpět: const ukazatel na typ T

const T * const ptr3 ; 

Ukazatel const na konstanta typu T. Nemůžete přesunout ukazatel ani změnit ukazatel typu dat na ukazatel. tj . ptr3-- ; ptr3++ ; *ptr3 = 19; nebude fungovat

3) Inteligentní ukazatele : {#include <memory>}

Sdílený ukazatel :

  T a ; 
     //shared_ptr<T> shptr(new T) ; not recommended but works 
     shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe

     std::cout << shptr.use_count() ; // 1 //  gives the number of " 
things " pointing to it. 
     T * temp = shptr.get(); // gives a pointer to object

     // shared_pointer used like a regular pointer to call member functions
      shptr->memFn();
     (*shptr).memFn(); 

    //
     shptr.reset() ; // frees the object pointed to be the ptr 
     shptr = nullptr ; // frees the object 
     shptr = make_shared<T>() ; // frees the original object and points to new object

Implementován pomocí referenčního počítání pro sledování toho, kolik "věcí" ukazuje na objekt, na který ukazuje ukazatel. Když tento počet přejde na 0, objekt se automaticky smaže, tj. Objekt se smaže, když všechny ukazatele share_ptr na objekt zmizí mimo rozsah. To se zbaví bolesti hlavy, že budete muset odstranit objekty, které jste přidělili pomocí nového.

Slabý ukazatel: Pomáhá řešit cyklický odkaz, který vzniká při použití sdíleného ukazatele Pokud máte dva objekty, na které odkazují dva sdílené ukazatele, a existuje vnitřní sdílený ukazatel směřující ke každému jinému sdílenému ukazateli, pak bude cyklický odkaz a objekt nebude odstraněn, pokud sdílené ukazatele zmizí mimo rozsah. Chcete-li to vyřešit, změňte interní člen ze sdíleného_ptr na slabý_ptr. Poznámka: Pro přístup k elementu, na který ukazuje slabý ukazatel, použijte lock (), což vrací slabý_ptr.

T a ; 
shared_ptr<T> shr = make_shared<T>() ; 
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr 
wk.lock()->memFn() ; // use lock to get a shared_ptr 
//   ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access

Viz: Kdy je std :: strong_ptr užitečné?

Unikátní ukazatel: Lehký inteligentní ukazatel s výhradním vlastnictvím. Použijte, když ukazatel ukazuje na jedinečné objekty bez sdílení objektů mezi ukazateli.

unique_ptr<T> uptr(new T);
uptr->memFn(); 

//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr 

Chcete-li změnit objekt, na který odkazuje jedinečný ptr, použijte sémantiku pohybu

unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1); 
// object pointed by uptr2 is deleted and 
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null 

Odkazy: Mohou být v podstatě i jako ukazatele const, tj. Ukazatel, který je konstantní a nemůže být přesunut s lepší syntaxí.

Viz: Jaké jsou rozdíly mezi proměnnou ukazatele a referenční proměnnou v jazyce C++?

r-value reference : reference to a temporary object   
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified 

Odkaz: https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ Děkuji Andre za poukázání na tuto otázku.

5
nnrales

Inteligentní ukazatel je třída, obálka normálního ukazatele. Na rozdíl od běžných ukazatelů je životní cyklus inteligentního bodu založen na počtu referencí (kolikrát je objekt inteligentního ukazatele přiřazen). Takže kdykoliv je inteligentní ukazatel přiřazen jinému, vnitřní počet referencí plus plus. A kdykoliv objekt vyjde mimo rozsah, počet referencí mínus minus.

Automatický ukazatel, i když vypadá podobně, je naprosto odlišný od inteligentního ukazatele. Jedná se o vhodnou třídu, která prostředek uvolní vždy, když objekt automatického ukazatele opustí proměnný rozsah. Do jisté míry vytvoří ukazatel (na dynamicky přidělenou paměť) podobnou proměnné zásobníku (staticky přidělené v době kompilace).

3
Trombe

Inteligentní ukazatele jsou ty, kde se nemusíte starat o přidělení paměti, sdílení zdrojů a přenos.

Tyto ukazatele můžete velmi dobře použít podobně jako alokace v jazyce Java. V Java Garbage Collector dělá trik, zatímco v Smart Pointers, trik provádí Destructors.

2
Daksh

Existující odpovědi jsou dobré, ale nepokrývají, co dělat, když chytrý ukazatel není (úplnou) odpovědí na problém, který se snažíte vyřešit.

Mimo jiné (vysvětleno v jiných odpovědích) pomocí inteligentního ukazatele je možné řešení Jak používat abstraktní třídu jako typ návratu funkce? který byl označen jako duplikát této otázky. První otázka, zda je v pokušení specifikovat abstraktní (nebo ve skutečnosti jakoukoliv) základní třídu jako návratový typ v jazyce C++, je však "co opravdu myslíte?". V dokumentaci knihovny kontejneru boost pointer je dobrá diskuse (s dalšími odkazy) idiomatického objektově orientovaného programování v C++ (a jak se liší od ostatních jazyků). V souhrnu, v C + + musíte přemýšlet o vlastnictví. Které inteligentní ukazatele vám pomohou, ale nejsou jediným řešením, nebo vždy úplným řešením (nedávají vám polymorfní kopii) a nejsou vždy řešením, které chcete vystavit ve svém rozhraní (a funkce vrátí zvuky jako rozhraní). Může být například dostatečné vrátit odkaz. Ale ve všech těchto případech (inteligentní ukazatel, ukazatel ukazatele nebo jednoduše vrácení odkazu) jste změnili návrat z hodnoty na nějakou formu reference . Pokud opravdu potřebujete kopii, budete možná potřebovat přidat více kotvové desky "idiom" nebo se přesunout za idiomatický (nebo jinak) OOP v C++ na obecnější polymorfismus pomocí knihoven jako Adobe Poly nebo Boost. TypeErasure .

1
da77a