Smart pekare

Den aktuella versionen av sidan har ännu inte granskats av erfarna bidragsgivare och kan skilja sig väsentligt från versionen som granskades den 22 mars 2021; kontroller kräver 17 redigeringar .

En  smart pekare är ett minnesinriktat idiom som används ofta vid programmering i högnivåspråk som C++ , Rust , och så vidare. Som regel implementeras den som en specialiserad klass (vanligtvis parametriserad ), som efterliknar gränssnittet för en vanlig pekare och lägger till den nödvändiga nya funktionaliteten (till exempel kontroll av gränser för åtkomst eller minnesrensning ) [1] .

Vanligtvis är huvudsyftet med att använda smarta pekare att kapsla in dynamisk minneshantering på ett sådant sätt att egenskaperna och beteendet hos smarta pekare efterliknar egenskaperna och beteendet hos vanliga pekare. Samtidigt är de ansvariga för att tilldelade resurser släpps i rätt tid och korrekt, vilket förenklar kodutveckling och felsökningsprocess, eliminerar minnesläckor och förekomsten av dinglande länkar [2] .

Pekare för delat ägande (med referensräkning)

Dessa används ofta med objekt som har speciella operationer "öka referensantal" ( AddRef()i COM ) och "minska referensantal" ( Release()i COM). Oftast ärvs sådana objekt från en speciell klass eller gränssnitt (till exempel IUnknowni COM).

När en ny referens till ett objekt dyker upp anropas operationen "öka antalet referenser" och när den förstörs anropas operationen "minska antalet referenser". Om antalet referenser till ett objekt blir noll, som ett resultat av operationen "reducera referenser", raderas objektet.

Denna teknik kallas automatisk referensräkning . Det matchar antalet pekare som lagrar objektets adress med antalet referenser som är lagrade i objektet, och när detta nummer når noll gör det att objektet tas bort. Dess fördelar är relativt hög tillförlitlighet, snabbhet och enkel implementering i C++ . Nackdelen är att det blir svårare att använda vid cirkulära referenser (behovet av att använda "svaga referenser").

Implementeringar

Det finns två typer av sådana pekare: med diskförvaring inuti objektet och med diskförvaring utanför.

Det enklaste alternativet är att lagra räknaren inuti ett hanterat objekt. I COM implementeras referensräknade objekt enligt följande:

Implementerat på samma sätt boost::intrusive_ptr.

Referensräknarna std::shared_ptrlagras utanför objektet, i en speciell datastruktur. En sådan smart pekare är dubbelt så stor som en standard (den har två fält, ett pekar på räknarstrukturen, det andra på det hanterade objektet). Denna design tillåter:

Eftersom räknarstrukturen är liten kan den till exempel allokeras genom objektpoolen .

Problemet med cirkulära referenser

Anta att det finns två objekt och vart och ett av dem har en ägande pekare. Pekaren i det första objektet tilldelas adressen till det andra objektet, och pekaren i det andra är adressen till det första objektet. Om nu alla externa (det vill säga inte lagrade inuti dessa objekt) pekare till två givna objekt tilldelas nya värden, så kommer pekarna inuti objekten fortfarande att äga varandra och kommer att finnas kvar i minnet. Som ett resultat kommer det att uppstå en situation där objekt inte kan nås, det vill säga en minnesläcka .

Problemet med cirkulära referenser löses antingen genom lämplig utformning av datastrukturer eller genom att använda sophämtning eller genom att använda två typer av referenser: stark (ägande) och svag (ej ägande, till exempel std::weak_ptr).

Implementeringsexempel

Markörer för enskild firma

Ofta är pekare för delat ägande för stora och "tunga" för programmerarens uppgifter: till exempel måste du skapa ett objekt av en av N-typer, äga det, komma åt dess virtuella funktioner från tid till annan och sedan korrekt radera det. För att göra detta, använd "lillebror" - en indikator på ensam ägande.

Sådana pekare när man tilldelar ett nytt värde eller tar bort sig själva tar bort objektet. Tilldelning av pekare för enskild firma är endast möjlig om en av pekarna förstörs - det kommer alltså aldrig att finnas en situation att två pekare äger samma objekt.

Deras nackdel är svårigheten att föra ett föremål utanför pekarens omfång.

Implementeringsexempel

Pekare till någon annans minnesbuffert

I de flesta fall, om det finns en funktion som hanterar en array, skrivs en av två saker:

void sort ( size_t size , int * data ); // pekare + storlek void sort ( std :: vektor < int >& data ); // specifik minnesstruktur

Den första utesluter automatisk avståndskontroll. Den andra begränsar tillämpligheten std::vectorav s, och du kan inte sortera till exempel en sträng av en array eller del av en annan vectors.

Därför använder de i utvecklade bibliotek för funktioner som använder andras minnesbuffertar "lätta" datatyper som

mall < classT > _ struct Buf1d { T * data ; storlek_t storlek ; Buf1d ( std :: vektor < T >& vec ); T & operator []( size_t i ); };

Används ofta för strängar: tolka , köra en textredigerare och andra specifika uppgifter behöver sina egna datastrukturer som är snabbare än vanliga strängmanipulationsmetoder.

Implementeringsexempel

  • standard mallbibliotek: std::string_view, std::span.
  • Qt: QStringView.

Anteckningar

  1. Alger D. Smarta pekare som ett idiom // C++. Programmerarens bibliotek . - 1999. - S. 75. - 320 sid. — ISBN 0-12-049942-8 . Arkiverad 12 juli 2018 på Wayback Machine
  2. Ivor Horton, Peter Van Weert. Råpekare och smarta pekare // Början C++17. Från nybörjare till proffs. - 5:a. - Apress, 2018. - P. 206. - ISBN 978-1-4842-3365-8 .

Länkar