Gedeelde pointers doorgeven als argumenten

Als ik een object declareer in een gedeelde aanwijzer:

std::shared_ptr<myClass> myClassObject(new myClass());

toen wilde ik het als argument doorgeven aan een methode:

DoSomething(myClassObject);
//the called method
void DoSomething(std::shared_ptr<myClass> arg1)
{
   arg1->someField = 4;
}

Verhoogt het bovenstaande gewoon het aantal referenties van de shared_pt en is alles cool? Of laat het een bungelende aanwijzer achter?

Moet je dit nog steeds doen?:

DoSomething(myClassObject.Get());
void DoSomething(std::shared_ptr<myClass>* arg1)
{
   (*arg1)->someField = 4;
}

Ik denk dat de 2e manier efficiënter kan zijn omdat het maar 1 adres hoeft te kopiëren (in tegenstelling tot de hele slimme aanwijzer), maar de 1e manier lijkt leesbaarder en ik verwacht niet dat de prestatielimieten worden overschreden. Ik wil er gewoon zeker van zijn dat er niets gevaarlijks aan is.

Bedankt.


Antwoord 1, autoriteit 100%

Ik wil een gedeelde pointer doorgeven aan een functie. Kun je me daarbij helpen?

Natuurlijk kan ik je daarbij helpen. Ik neem aan dat je enig begrip hebt van eigendomssemantiek in C++. Is dat waar?

Ja, ik voel me redelijk op mijn gemak met het onderwerp.

Goed.

Ok, ik kan maar twee redenen bedenken om een ​​shared_ptrargument te gebruiken:

  1. De functie wil het eigendom van het object delen;
  2. De functie voert een bewerking uit die specifiek werkt op shared_ptrs.

In welke ben je geïnteresseerd?

Ik ben op zoek naar een algemeen antwoord, dus ik ben eigenlijk in beide geïnteresseerd. Ik ben wel benieuwd wat je bedoelt in geval #2.

Voorbeelden van dergelijke functies zijn std::static_pointer_cast, aangepaste vergelijkers of predikaten. Als u bijvoorbeeld alle unieke shared_ptr van een vector moet vinden, heeft u zo’n predikaat nodig.

Ah, als de functie de slimme aanwijzer zelf moet manipuleren.

Precies.

In dat geval denk ik dat we de referentie moeten doorgeven.

Ja. En als het de aanwijzer niet verandert, wil je de const-referentie doorgeven. Het is niet nodig om te kopiëren, omdat u geen eigendom hoeft te delen. Dat is het andere scenario.

Ok, begrepen. Laten we het over het andere scenario hebben.

Degene waarvan u het eigendom deelt? OK. Hoe deel je het eigendom met shared_ptr?

Door het te kopiëren.

Dan moet de functie een kopie maken van een shared_ptr, correct?

Natuurlijk. Dus ik geef het door met een verwijzing naar const en kopieer naar een lokale variabele?

Nee, dat is een pessimisering. Als het door verwijzing wordt doorgegeven, heeft de functie geen andere keuze dan de kopie handmatig te maken. Als het op waarde wordt doorgegeven, kiest de compiler de beste keuze tussen een kopie en een zet en voert deze automatisch uit. Dus geef de waarde door.

Goed punt. Ik moet onthouden dat “Wilt u snelheid? Geef uw waarde door.” artikel vaker.

Wacht, wat als de functie bijvoorbeeld de shared_ptropslaat in een lidvariabele? Maakt dat geen overbodige kopie?

De functie kan het argument shared_ptreenvoudig naar zijn opslag verplaatsen. Het verplaatsen van een shared_ptris goedkoop omdat het geen referentietellingen verandert.

Ah, goed idee.

Maar ik denk aan een derde scenario: wat als je de shared_ptrniet wilt manipuleren, noch het eigendom wilt delen?

In dat geval is shared_ptrvolledig irrelevant voor de functie. Als je de pointee wilt manipuleren, neem dan een pointee en laat de bellers kiezen welke eigendomssemantiek ze willen.

En moet ik de pointe als referentie nemen of op waarde?

De gebruikelijke regels zijn van toepassing. Slimme aanwijzers veranderen niets.

Geef de waarde door als ik ga kopiëren, geef door als ik een kopie wil vermijden.

Juist.

Hmm. Ik denk dat je nog een ander scenario bent vergeten. Wat als ik het eigendom wil delen, maar alleen afhankelijk van een bepaalde voorwaarde?

Ah, een interessant randgeval. Ik verwacht niet dat dat vaak zal gebeuren. Maar als het gebeurt, kun je ofwel de waarde doorgeven en de kopie negeren als je hem niet nodig hebt, of de referentie doorgeven en de kopie maken als je hem nodig hebt.

Ik riskeer één overtollige kopie bij de eerste optie en verlies een mogelijke zet in de tweede. Kan ik de taart niet opeten en ook krijgen?

Als u zich in een situatie bevindt waarin dat er echt toe doet, kunt u twee overbelastingen bieden, de ene met een const lvalu-referentie en de andere met een rvalue-referentie. De een kopieert, de ander beweegt. Een perfect-forwarding-functiesjabloon is een andere optie.

Ik denk dat dit alle mogelijke scenario’s dekt. Heel erg bedankt.


Antwoord 2, autoriteit 14%

Ik denk dat mensen onnodig bang zijn om onbewerkte pointers als functieparameters te gebruiken. Als de functie de aanwijzer niet opslaat of de levensduur ervan op een andere manier beïnvloedt, werkt een onbewerkte aanwijzer net zo goed en vertegenwoordigt deze de kleinste gemene deler. Overweeg bijvoorbeeld hoe u een unique_ptrzou doorgeven aan een functie die een shared_ptrals parameter nodig heeft, hetzij door waarde of door const-referentie?

void DoSomething(myClass * p);
DoSomething(myClass_shared_ptr.get());
DoSomething(myClass_unique_ptr.get());

Een onbewerkte aanwijzer als functieparameter weerhoudt je er niet van om slimme aanwijzers te gebruiken in de aanroepende code, waar het er echt toe doet.


Antwoord 3, autoriteit 3%

Ja, het hele idee over een shared_ptr<> is dat meerdere instanties dezelfde onbewerkte aanwijzer kunnen bevatten en dat het onderliggende geheugen alleen wordt vrijgemaakt wanneer de laatste instantie van shared_ptr<> wordt vernietigd.

Ik zou een verwijzing naar een shared_ptr<> omdat dat het doel verslaat, omdat je nu weer met raw_pointers te maken hebt.


Antwoord 4

Voorwaarde doorgeven in uw eerste voorbeeld is veilig, maar er is een beter idioom. Geef waar mogelijk door aan const-referentie – ik zou ja zeggen, zelfs als ik te maken heb met slimme aanwijzingen. Je tweede voorbeeld is niet helemaal kapot, maar het is erg !???. Dwaas, niets bereiken en een deel van het punt van slimme aanwijzingen verslaan, en je in een wereld vol pijn met fouten achterlaten als je probeert dingen te negeren en aan te passen.


Antwoord 5

in uw functie
DoSomething
u wijzigt een gegevenslid van een instantie van klasse myClass
dus wat u aan het wijzigen bent, is het beheerde object (onbewerkte aanwijzer) en niet het object (shared_ptr). Wat betekent dat bij het terugkeerpunt van deze functie alle gedeelde verwijzingen naar de beheerde onbewerkte aanwijzer hun gegevenslid: myClass::someFieldin een andere waarde zullen zien veranderen.

in dit geval geef je een object door aan een functie met de garantie dat je het niet wijzigt (we hebben het over shared_ptr, niet het eigendomsobject).

Het idioom om dit uit te drukken is door: een const ref, like so

void DoSomething(const std::shared_ptr<myClass>& arg)

Evenzo verzekert u de gebruiker van uw functie dat u geen andere eigenaar toevoegt aan de lijst met eigenaren van de onbewerkte aanwijzer. U hebt echter de mogelijkheid behouden om het onderliggende object waarnaar wordt verwezen door de onbewerkte aanwijzer te wijzigen.

CAVEAT: Wat betekent dat als iemand op de een of andere manier shared_ptr::resetaanroept voordat je je functie aanroept, en het op dat moment de laatste shared_ptr is die de raw_ptr bezit, je object wordt vernietigd , en je functie manipuleert een bungelende aanwijzer naar een vernietigd object. ZEER GEVAARLIJK!!!

Other episodes