Vraag Wat is een slimme aanwijzer en wanneer zou ik er een moeten gebruiken?


Wat is een slimme aanwijzer en wanneer zou ik er een moeten gebruiken?


1427
2017-09-20 00:09


oorsprong


antwoorden:


Een slimme pointer is een klasse die een 'ruwe' (of 'blote') C ++ -aanwijzer omgeeft om de levensduur van het object te bepalen waarnaar wordt verwezen. Er is geen enkel slim aanwijzertype, maar ze proberen allemaal een onbewerkte aanwijzer op een praktische manier te abstraheren.

Slimme aanwijzers verdienen de voorkeur boven onbewerkte aanwijzers. Als u merkt dat u pointers moet gebruiken (denk eerst aan uzelf werkelijk ), zou u normaal gesproken een slimme aanwijzer willen gebruiken, omdat dit veel van de problemen met onbewerkte aanwijzers kan verminderen, waarbij u vooral vergeet om het object en het lekkende geheugen te verwijderen.

Met onbewerkte wijzers moet de programmeur het object expliciet vernietigen wanneer het niet langer nuttig is.

// 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...?

Een slimme pointer door vergelijking definieert een beleid ten aanzien van wanneer het object wordt vernietigd. Je moet het object nog steeds maken, maar je hoeft je niet langer zorgen te maken over het vernietigen ervan.

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

Het eenvoudigste beleid dat wordt gebruikt, betreft de scope van het slimme pointer-wrapper-object, zoals geïmplementeerd door boost::scoped_ptr of std::unique_ptr.

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

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

Let daar op scoped_ptr instanties kunnen niet worden gekopieerd. Dit voorkomt dat de aanwijzer meerdere keren wordt verwijderd (onjuist). U kunt echter verwijzingen ernaar doorgeven aan andere functies die u belt.

Scoped pointers zijn handig als u de levensduur van het object wilt koppelen aan een bepaald codeblok, of als u het als lidgegevens in een ander object, de levensduur van dat andere object, heeft ingesloten. Het object bestaat totdat het bevattende codeblok is afgesloten of totdat het bijbehorende object zelf is vernietigd.

Een ingewikkelder slim pointerbeleid omvat het tellen van de aanwijzer. Hierdoor kan de aanwijzer worden gekopieerd. Wanneer de laatste "verwijzing" naar het object wordt vernietigd, wordt het object verwijderd. Dit beleid wordt geïmplementeerd door boost::shared_ptr en 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.

Verwijzingen getelde aanwijzers zijn erg handig wanneer de levensduur van uw object veel gecompliceerder is en niet rechtstreeks gebonden is aan een bepaald deel van de code of aan een ander object.

Er is één nadeel aan verwijzingen met referenties: de mogelijkheid om een ​​bungelende referentie te maken:

// 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!

Een andere mogelijkheid is het maken van ronde verwijzingen:

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

boost::shared_ptr<Owner> p1 (new Owner());
boost::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!

Om dit probleem te omzeilen, hebben zowel Boost als C ++ 11 een a gedefinieerd weak_ptr om een ​​zwakke (ontcijferde) verwijzing naar a te definiëren shared_ptr.


BIJWERKEN

Dit antwoord is nogal oud en beschrijft dus wat 'goed' was in die tijd, wat slimme tips waren van de Boost-bibliotheek. Sinds C ++ 11 heeft de standaardbibliotheek voldoende slimme aanwijzertypen beschikbaar gesteld, dus u moet het gebruik van de voorkeur geven std::unique_ptr, std::shared_ptr en std::weak_ptr.

Er is ook std::auto_ptr. Het lijkt veel op een scoped aanwijzer, behalve dat het ook het "speciale" gevaarlijke vermogen heeft om gekopieerd te worden - wat ook onverwacht het eigendom overdraagt! Het is verouderd in de nieuwste normen, dus u zou het niet moeten gebruiken. Gebruik de std::unique_ptr in plaats daarvan.

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.

1640
2017-09-20 00:48



Hier is een eenvoudig antwoord voor deze dagen van moderne C ++:

  • Wat is een slimme aanwijzer? 
    Het is een type waarde dat als een aanwijzer kan worden gebruikt, maar biedt de extra functie van automatisch geheugenbeheer: wanneer de aanwijzer niet langer in gebruik is, wordt het geheugen waarnaar het verwijst, uit de toewijzing verwijderd (zie ook de meer gedetailleerde definitie op Wikipedia).
  • Wanneer moet ik er een gebruiken? 
    In code die inhoudt dat het eigendom van een stukje geheugen wordt gevolgd, toewijzen of de-alloceren; de slimme aanwijzer bespaart u vaak de behoefte om deze dingen expliciet te doen.
  • Maar welke slimme wijzer moet ik gebruiken in welke van die gevallen?
    • Gebruik std::unique_ptr wanneer u niet van plan bent meerdere verwijzingen naar hetzelfde object te houden. Gebruik het bijvoorbeeld voor een aanwijzer naar het geheugen dat wordt toegewezen bij het invoeren van een bepaald bereik en dat bij toewijzing van de scope wordt gede-alloceerd.
    • Gebruik std::shared_ptr wanneer je vanuit meerdere plaatsen naar je object wilt verwijzen - en niet wilt dat het wordt gede-alloceerd totdat al deze referenties zelf verdwenen zijn.
    • Gebruik std::weak_ptr wanneer je vanuit meerdere plaatsen naar je object wilt verwijzen - voor die referenties waarvoor het ok is om te negeren en deallocatie (dus zullen ze gewoon opmerken dat het object weg is wanneer je probeert te dereferen).
    • Gebruik de boost:: slimme aanwijzers of std::auto_ptr behalve in speciale gevallen die je kunt lezen als dat nodig is.
  • Hé, ik heb niet gevraagd welke te gebruiken! 
    Ah, maar je wilde het echt, geef het toe.
  • Dus wanneer zou ik dan gewone pointers moeten gebruiken? 
    Meestal in code die zich niet bewust is van het eigenaarschap van het geheugen. Dit zou typisch in functies zijn die een pointer van ergens anders krijgen en niet toewijzen, de-alloceren of opslaan van een kopie van de pointer die hun uitvoering overleeft.

170
2018-05-09 19:06



Slimme aanwijzer is een wijzerachtig type met wat extra functionaliteit, b.v. automatische geheugen deallocatie, referentie tellen etc.

Kleine intro is beschikbaar op pagina Smart Pointers - Wat, Waarom, Welke?.

Een van de eenvoudige smart-pointer-typen is std::auto_ptr (hoofdstuk 20.4.5 van de standaard C ++), waarmee het geheugen automatisch kan worden gealloceerd wanneer het buiten het bereik valt en dat robuuster is dan eenvoudig aanwijsgebruik wanneer uitzonderingen worden gegenereerd, hoewel minder flexibel.

Een ander handig type is boost::shared_ptr die referentietelling implementeert en automatisch geheugen deallocieert wanneer er geen verwijzingen naar objecten zijn. Dit helpt geheugenlekken te voorkomen en is eenvoudig te gebruiken om te implementeren RAII.

Het onderwerp wordt in het boek uitgebreid besproken "C ++ Templates: The Complete Guide" door David Vandevoorde, Nicolai M. Josuttis, hoofdstuk 20. Slimme aanwijzers. Sommige onderwerpen kwamen aan bod:


96
2017-09-20 00:32



Definities van Chris, Sergdev en Llyod zijn correct. Ik geef de voorkeur aan een eenvoudigere definitie, gewoon om mijn leven eenvoudig te houden: Een slimme aanwijzer is gewoon een klasse die de overbelast ->en * operators. Dat betekent dat je object semantisch lijkt op een aanwijzer, maar je kunt het veel coolere dingen laten doen, zoals referentietelling, automatische vernietiging, enzovoort. shared_ptr en auto_ptr zijn in de meeste gevallen voldoende, maar komen met hun eigen reeks kleine eigenaardigheden.


33
2017-09-20 01:53



Een slimme muisaanwijzer lijkt op een gewone (getypte) aanwijzer, zoals 'char *', behalve wanneer de aanwijzer zelf buiten bereik raakt en ook waarheen hij verwijst, ook wordt verwijderd. U kunt het gebruiken als een gewone aanwijzer, door "->" te gebruiken, maar niet als u een echte aanwijzer naar de gegevens nodig hebt. Daarvoor kunt u "& * ptr" gebruiken.

Het is handig voor:

  • Objecten die met nieuw moeten worden toegewezen, maar die u dezelfde levensduur wilt hebben als iets op die stapel. Als het object is toegewezen aan een slimme aanwijzer, worden deze verwijderd wanneer het programma die functie of dat blok afsluit.

  • Gegevensleden van klassen, zodat wanneer het object wordt verwijderd ook alle gegevens van de eigenaar worden verwijderd, zonder speciale code in de destructor (u moet er zeker van zijn dat de destructor virtueel is, wat bijna altijd een goede zaak is om te doen) .

Je kan niet wil een slimme pointer gebruiken wanneer:

  • ... de aanwijzer zou eigenlijk niet de eigenaar van de gegevens moeten zijn ... d.w.z. wanneer u alleen de gegevens gebruikt, maar u wilt dat deze de functie overleeft waarnaar u verwijst.
  • ... de slimme aanwijzer zelf zal op een gegeven moment niet worden vernietigd. U wilt niet dat het in het geheugen zit dat nooit wordt vernietigd (zoals in een object dat dynamisch is toegewezen maar niet expliciet wordt verwijderd).
  • ... twee slimme wijzers wijzen mogelijk op dezelfde gegevens. (Er zijn echter nog slimmere aanknopingspunten voor dat ... dat wordt genoemd referentie tellen.)

Zie ook:


26
2017-09-20 00:13



De meeste soorten slimme aanwijzers zijn verantwoordelijk voor het weggooien van het pointer-to-object voor u. Het is erg handig omdat u niet meer hoeft na te denken over het handmatig weggooien van objecten.

De meest gebruikte slimme wijzers zijn std::tr1::shared_ptr (of boost::shared_ptr), en, minder gebruikelijk, std::auto_ptr. Ik raad aan regelmatig gebruik van shared_ptr.

shared_ptr is zeer veelzijdig en behandelt een grote verscheidenheid aan afvalverwijderingsscenario's, inclusief gevallen waarin objecten "DLL-grenzen" moeten worden gepasseerd (de veel voorkomende nachtmerrie-zaak, indien verschillend libcs worden gebruikt tussen uw code en de DLL's).


14
2017-09-20 00:14



Een slimme aanwijzer is een object dat als een aanwijzer fungeert, maar bovendien controle biedt over constructie, vernietiging, kopiëren, verplaatsen en dereferencing.

Men kan zijn eigen slimme pointer implementeren, maar veel bibliotheken bieden ook slimme pointerimplementaties met elk verschillende voordelen en nadelen.

Bijvoorbeeld, boost biedt de volgende slimme pointer-implementaties:

  • shared_ptr<T> is een verwijzing naar T een referentietelling gebruiken om te bepalen wanneer het object niet langer nodig is.
  • scoped_ptr<T> wordt een aanwijzer automatisch verwijderd wanneer deze buiten het bereik valt. Geen toewijzing is mogelijk.
  • intrusive_ptr<T>is een andere referentie-telwijzer. Het levert betere prestaties dan shared_ptr, maar vereist het type T om een ​​eigen referentietelmechanisme te bieden.
  • weak_ptr<T> is een zwakke aanwijzer, in samenwerking met shared_ptr om circulaire verwijzingen te voorkomen.
  • shared_array<T> is als shared_ptr, maar voor arrays van T.
  • scoped_array<T> is als scoped_ptr, maar voor arrays van T.

Dit zijn slechts één lineaire beschrijvingen van elk en kunnen vanaf behoefte worden gebruikt, voor meer detail en voorbeelden kan men de documentatie van Boost bekijken.

Bovendien biedt de C ++ standaardbibliotheek drie slimme wijzers; std::unique_ptr voor uniek eigendom, std::shared_ptr voor gedeeld eigendom en std::weak_ptr. std::auto_ptr bestond in C ++ 03 maar is nu verouderd.


14
2018-03-12 09:51



Hier is de link voor vergelijkbare antwoorden: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

Een slimme aanwijzer is een object dat werkt, eruit ziet en aanvoelt als een normale aanwijzer, maar meer functionaliteit biedt. In C ++ worden slimme aanwijzers geïmplementeerd als sjabloonklassen die een aanwijzer inkapselen en standaardaanwijzeroperatoren negeren. Ze hebben een aantal voordelen ten opzichte van reguliere aanwijzingen. Ze worden gegarandeerd geïnitialiseerd als nul-verwijzingen of verwijzingen naar een heap-object. Indirectie door een lege aanwijzer wordt gecontroleerd. Verwijderen is nooit nodig. Objecten worden automatisch vrijgegeven wanneer de laatste muisaanwijzer is verdwenen. Een belangrijk probleem met deze slimme aanwijzers is dat ze, in tegenstelling tot reguliere verwijzingen, nalatenschap niet respecteren. Slimme verwijzingen zijn onaantrekkelijk voor polymorfe code. Hieronder is een voorbeeld gegeven voor de implementatie van slimme aanwijzers.

Voorbeeld: 

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:
               //...
};

Deze klasse implementeert een slimme aanwijzer naar een object van type X. Het object zelf bevindt zich op de heap. Hier is hoe het te gebruiken:

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

Zoals andere overbelaste operators gedraagt ​​p zich als een normale aanwijzer,

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

9
2018-03-07 09:03