wake-up-neo.com

C++ - Weitergabe von Referenzen an std :: shared_ptr oder boost :: shared_ptr

Wenn ich eine Funktion habe, die mit einem shared_ptr arbeiten muss, wäre es nicht effizienter, ihr einen Verweis darauf zu übergeben (um das shared_ptr-Objekt nicht zu kopieren)? Was sind die möglichen negativen Nebenwirkungen? .__ Ich stelle mir zwei mögliche Fälle vor:

1) Innerhalb der Funktion wird eine Kopie des Arguments erstellt, wie in

ClassA::take_copy_of_sp(boost::shared_ptr<foo> &sp)  
{  
     ...  
     m_sp_member=sp; //This will copy the object, incrementing refcount  
     ...  
}  

2) Innerhalb der Funktion wird das Argument nur verwendet, wie in 

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}  

Ich sehe in beiden Fällen keinen guten Grund, den boost::shared_ptr<foo> nach Wert statt nach Referenz zu übergeben. Wenn Sie den Wert übergeben, wird der Referenzzähler aufgrund des Kopierens nur "vorübergehend" inkrementiert und dann beim Beenden des Funktionsumfangs dekrementiert Überspringe ich etwas?

Nur zur Klarstellung, nachdem ich einige Antworten gelesen habe: Ich stimme den vorzeitigen Optimierungsproblemen vollkommen zu und versuche immer, zuerst einen Hot-Spot zu erstellen, der dann an den Hot-Spots arbeitet. Meine Frage war mehr aus rein technischer Sicht, wenn Sie wissen, was ich meine.

113
abigagli

Der Sinn einer bestimmten shared_ptr-Instanz besteht darin, (soweit möglich) zu gewährleisten, dass das Objekt, auf das sie verweist, noch vorhanden ist, solange sich dieser shared_ptr im Gültigkeitsbereich befindet, da sein Referenzzähler mindestens 1 beträgt.

Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
    // sp points to an object that cannot be destroyed during this function
}

Wenn Sie also einen Verweis auf einen shared_ptr verwenden, deaktivieren Sie diese Garantie. Also in deinem zweiten Fall:

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}

Woher wissen Sie, dass sp->do_something() aufgrund eines Nullzeigers nicht explodiert?

Es hängt alles davon ab, was sich in diesen "..." - Abschnitten des Codes befindet. Was ist, wenn Sie während des ersten "..." etwas aufrufen, das den Nebeneffekt hat (irgendwo in einem anderen Teil des Codes), einen shared_ptr für dasselbe Objekt zu löschen? Und was ist, wenn es der einzige verbleibende shared_ptr für dieses Objekt ist? Bye bye Objekt, genau dort, wo Sie es versuchen und benutzen wollen.

Es gibt also zwei Möglichkeiten, diese Frage zu beantworten:

  1. Untersuchen Sie die Quelle Ihres gesamten Programms sehr sorgfältig, bis Sie sicher sind, dass das Objekt während des Funktionskörpers nicht stirbt.

  2. Ändern Sie den Parameter zurück, sodass er ein eindeutiges Objekt anstelle einer Referenz ist.

Allgemeiner Ratschlag, der hier zutrifft: Machen Sie sich nicht die Mühe, um der Leistung willen riskante Änderungen an Ihrem Code vorzunehmen, bis Sie Ihr Produkt in einer realistischen Situation in einem Profiler zeitlich festgelegt und abschließend gemessen haben, dass die von Ihnen gewünschte Änderung eine positive Auswirkung hat signifikanter Unterschied zur Leistung.

pdate für Kommentator JQ

Hier ist ein ausgedachtes Beispiel. Es ist bewusst einfach, so dass der Fehler offensichtlich sein wird. In realen Beispielen ist der Fehler nicht so offensichtlich, weil er in Schichten realer Details verborgen ist.

Wir haben eine Funktion, die irgendwo eine Nachricht sendet. Es kann sich um eine große Nachricht handeln. Anstatt also einen std::string zu verwenden, der wahrscheinlich kopiert wird, wenn er an mehrere Stellen weitergegeben wird, verwenden wir einen shared_ptr für einen String:

void send_message(std::shared_ptr<std::string> msg)
{
    std::cout << (*msg.get()) << std::endl;
}

(Wir "senden" es nur an die Konsole für dieses Beispiel).

Jetzt möchten wir eine Funktion hinzufügen, um die vorherige Nachricht zu speichern. Wir wollen das folgende Verhalten: Eine Variable muss existieren, die die zuletzt gesendete Nachricht enthält, aber während eine Nachricht gerade gesendet wird, darf es keine vorherige Nachricht geben (die Variable sollte vor dem Senden zurückgesetzt werden). Also deklarieren wir die neue Variable:

std::shared_ptr<std::string> previous_message;

Dann ändern wir unsere Funktion gemäß den von uns festgelegten Regeln:

void send_message(std::shared_ptr<std::string> msg)
{
    previous_message = 0;
    std::cout << *msg << std::endl;
    previous_message = msg;
}

Bevor wir mit dem Senden beginnen, verwerfen wir die aktuelle vorherige Nachricht, und nachdem der Sendevorgang abgeschlossen ist, können wir die neue vorherige Nachricht speichern. Alles gut. Hier ist ein Testcode:

send_message(std::shared_ptr<std::string>(new std::string("Hi")));
send_message(previous_message);

Und wie erwartet wird Hi! zweimal gedruckt.

Nun kommt Herr Maintainer, der sich den Code ansieht und denkt: Hey, dieser Parameter für send_message ist ein shared_ptr:

void send_message(std::shared_ptr<std::string> msg)

Das kann natürlich geändert werden in:

void send_message(const std::shared_ptr<std::string> &msg)

Denken Sie an die Leistungssteigerung, die dies bringen wird! (Es ist egal, dass wir über einen Kanal eine normalerweise große Nachricht senden, sodass die Leistungssteigerung so gering ist, dass sie nicht messbar ist.).

Das eigentliche Problem ist jedoch, dass der Testcode jetzt undefiniertes Verhalten aufweist (in Visual C++ 2010-Debugbuilds stürzt er ab).

Herr Maintainer ist überrascht, fügt jedoch send_message eine Defensivprüfung hinzu, um das Problem zu stoppen:

void send_message(const std::shared_ptr<std::string> &msg)
{
    if (msg == 0)
        return;

Aber natürlich geht es weiter und stürzt ab, weil msg beim Aufruf von send_message niemals null ist.

Wie gesagt, bei all dem Code, der in einem trivialen Beispiel so nahe beieinander liegt, ist es leicht, den Fehler zu finden. In realen Programmen mit komplexeren Beziehungen zwischen veränderlichen Objekten, die Zeiger zueinander enthalten, ist es jedoch einfach, den Fehler zu machen und den Fehler zu konstruieren notwendige Testfälle, um den Fehler zu erkennen.

Die einfache Lösung, bei der eine Funktion sich auf einen shared_ptr verlassen soll, der weiterhin durchgehend nicht null ist, besteht darin, dass die Funktion ihren eigenen wahren shared_ptr zuweist, anstatt sich auf einen Verweis auf einen vorhandenen shared_ptr zu verlassen.

Der Nachteil ist, dass das Kopieren eines shared_ptr nicht kostenlos ist: Selbst "sperrenfreie" Implementierungen müssen eine ineinandergreifende Operation verwenden, um Threading-Garantien zu erfüllen. Es kann also Situationen geben, in denen ein Programm erheblich beschleunigt werden kann, indem ein shared_ptr in einen shared_ptr & geändert wird. Dies ist jedoch keine Änderung, die sicher an allen Programmen vorgenommen werden kann. Es ändert die logische Bedeutung des Programms.

Beachten Sie, dass ein ähnlicher Fehler auftreten würde, wenn wir std::string anstelle von std::shared_ptr<std::string> und anstelle von:

previous_message = 0;

um die Nachricht zu löschen, sagten wir:

previous_message.clear();

Dann wäre das Symptom das versehentliche Senden einer leeren Nachricht anstelle eines undefinierten Verhaltens. Die Kosten für eine zusätzliche Kopie einer sehr großen Zeichenfolge können erheblich höher sein als die Kosten für das Kopieren eines shared_ptr, sodass der Kompromiss möglicherweise anders ausfällt.

108

Ich stimmte der am besten bewerteten Antwort nicht zu, also suchte ich nach Expertenmeinungen, und hier sind sie ... von http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond -2011-Scott-Andrei-und-Herb-Ask-Us-Anything

Herb Sutter: "Wenn Sie shared_ptr's übergeben, sind Kopien teuer"

Scott Meyers: "Es gibt nichts Besonderes an shared_ptr, wenn es darum geht, ob Sie es als Wert übergeben oder als Referenz übergeben. Verwenden Sie genau die gleiche Analyse, die Sie für jeden anderen benutzerdefinierten Typ verwenden. Die Leute scheinen diese Wahrnehmung zu haben, die shared_ptr irgendwie löst Alle Managementprobleme, und das ist, weil es klein ist, zwingend kostengünstig, den Wert zu übergeben. Es muss kopiert werden, und es gibt Kosten, die damit verbunden sind ... Es ist teuer, den Wert zu übergeben, wenn ich also mitkommen kann es mit der richtigen Semantik in meinem Programm, ich werde es stattdessen durch Verweis auf const oder referenzieren "

Herb Sutter: "Übergeben Sie sie immer durch einen Verweis an const, und sehr selten, vielleicht weil Sie wissen, wie Ihr Anruf die Sache, von der Sie einen Verweis erhalten haben, ändert. Vielleicht könnten Sie dann einen Wert übergeben ... wenn Sie sie als Parameter kopieren, oh Meine Güte, Sie brauchen diese Referenzzählung fast nie zu übertreffen, weil sie sowieso am Leben gehalten wird, und Sie sollten sie als Referenz übergeben, also machen Sie das bitte. "

Update: Herb hat dies hier erweitert: http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/ , obwohl die Moral der Geschichte ist, dass Sie dies nicht tun sollten Geben Sie keine shared_ptr's weiter, es sei denn, Sie möchten den intelligenten Zeiger selbst verwenden oder bearbeiten, z. B. um den Eigentümer zu teilen oder zu übertragen.

109
Jon

Ich würde von dieser Praxis abraten, es sei denn, Sie und die anderen Programmierer, mit denen Sie wirklich zusammenarbeiten, wirklich wissen, was Sie alle tun.

Erstens haben Sie keine Ahnung, wie sich die Schnittstelle zu Ihrer Klasse entwickeln könnte, und Sie möchten verhindern, dass andere Programmierer schlechte Dinge tun. Das Übergeben eines shared_ptr als Referenz sollte von Programmierern nicht erwartet werden, da es nicht idiomatisch ist. Dies macht es leicht, es falsch zu verwenden. Programm defensiv: machen Sie die Verwendung der Schnittstelle schwierig. Das Passieren als Referenz führt später zu Problemen.

Zweitens optimieren Sie nicht, bis Sie wissen, dass diese bestimmte Klasse ein Problem sein wird. Profil zuerst, und dann, wenn Ihr Programm wirklich den Boost durch Referenzübergabe benötigt, dann vielleicht. Schwitzen Sie ansonsten nicht die kleinen Dinge (d. H. Die zusätzlichen N-Anweisungen, die zum Übergeben erforderlich sind), und sorgen Sie sich stattdessen um Design, Datenstrukturen, Algorithmen und langfristige Wartbarkeit.

22
Mark Kegel

Ja, eine Referenz zu nehmen ist dort in Ordnung. Sie beabsichtigen nicht, die Methode gemeinsam zu nutzen. es will nur damit arbeiten. Sie können auch eine Referenz für den ersten Fall verwenden, da Sie diese sowieso kopieren. Aber für den ersten Fall übernimmt es den Besitz . Es gibt diesen Trick, um es immer noch nur einmal zu kopieren:

void ClassA::take_copy_of_sp(boost::shared_ptr<foo> sp) {
    m_sp_member.swap(sp);
}

Sie sollten auch kopieren, wenn Sie es zurücksenden (d. H. Keine Referenz zurücksenden). Weil Ihre Klasse nicht weiß, was der Client damit macht (es könnte einen Zeiger darauf speichern und dann passiert ein Urknall). Wenn sich später herausstellt, dass es sich um einen Engpass handelt (erstes Profil!), Können Sie trotzdem eine Referenz zurückgeben.


Bearbeiten: Natürlich gilt dies, wie andere bereits betont haben, nur, wenn Sie Ihren Code kennen und wissen, dass Sie den übergebenen gemeinsamen Zeiger nicht in irgendeiner Weise zurücksetzen. Wenn Sie Zweifel haben, geben Sie einfach den Wert an.

Es ist sinnvoll, shared_ptrs über const& zu übergeben. Es wird wahrscheinlich keine Probleme verursachen (außer in dem unwahrscheinlichen Fall, dass der referenzierte shared_ptr während des Funktionsaufrufs gelöscht wird, wie von Earwicker beschrieben) und es wird wahrscheinlich schneller sein, wenn Sie viele davon herumgeben. Merken; Der Standardcode boost::shared_ptr ist threadsicher, das Kopieren enthält also ein threadsicheres Inkrement.

Verwenden Sie const& und nicht nur &, da temporäre Objekte möglicherweise nicht durch einen nicht konstanten Verweis übergeben werden. (Auch wenn eine Spracherweiterung in MSVC es Ihnen trotzdem erlaubt)

11
Magnus Hoff

Im zweiten Fall ist dies einfacher:

Class::only_work_with_sp(foo &sp)
{    
    ...  
    sp.do_something();  
    ...  
}

Sie können es als anrufen

only_work_with_sp(*sp);
10
Greg Rogers

Ich würde die Weitergabe eines gemeinsam genutzten Zeigers durch const reference befürworten - eine Semantik, die besagt, dass die mit dem Zeiger übergebene Funktion NICHT den Zeiger besitzt, was für Entwickler ein reines Idiom ist.

Die einzige Gefahr besteht in mehreren Thread-Programmen, dass das Objekt, auf das der gemeinsame Zeiger zeigt, in einem anderen Thread zerstört wird. Daher ist es sicher zu sagen, dass die Verwendung der const-Referenz des gemeinsam genutzten Zeigers in Single-Threaded-Programmen sicher ist.

Das Übergeben eines gemeinsam genutzten Zeigers durch eine nicht konstante Referenz ist manchmal gefährlich - der Grund sind die Swap- und Reset-Funktionen, die die Funktion im Inneren aufrufen kann, um das Objekt zu zerstören, das nach Rückkehr der Funktion noch als gültig gilt.

Ich denke, es geht nicht um vorzeitige Optimierung - es geht darum, unnötige Verschwendung von CPU-Zyklen zu vermeiden, wenn Sie sich klar sind, was Sie tun möchten und die Kodierungssprache von Ihren Entwickler-Entwicklern fest übernommen wurde.

Nur meine 2 Cent :-)

3
zmqiu

Es scheint, dass alle Vor-und Nachteile hier auf einen beliebigen Typ verallgemeinert werden können, der durch Verweis übergeben wird, nicht nur durch shared_ptr. Meiner Meinung nach sollten Sie die Semantik des Referenzierens, der Referenzreferenz und des Werts kennen und korrekt verwenden. Es ist jedoch absolut nichts inhärent falsch, shared_ptr als Referenz zu übergeben, es sei denn, Sie glauben, dass alle Referenzen schlecht sind ...

Um zum Beispiel zurückzukehren:

Class::only_work_with_sp( foo &sp ) //Again, no copy here  
{    
    ...  
    sp.do_something();  
    ...  
}

Woher wissen Sie, dass sp.do_something() nicht durch einen baumelnden Zeiger gesprengt wird?

Die Wahrheit ist, dass Shared_ptr oder nicht, const oder nicht, dies passieren kann, wenn Sie einen Konstruktionsfehler haben, z. B. das direkte oder indirekte Teilen des Eigentums von sp zwischen Threads, den Missbrauch eines Objekts, das delete this verwendet, Sie einen zirkularen Besitz oder einen anderen Besitz haben Fehler.

3
Sandy

Ich würde einen "einfachen" Verweis vermeiden, es sei denn, die Funktion kann den Zeiger explizit ändern. 

Ein const & kann eine sinnvolle Mikrooptimierung sein, wenn kleine Funktionen aufgerufen werden, z. um weitere Optimierungen zu ermöglichen, z. Auch das Inkrementieren/Dekrementieren - da es Thread-sicher ist - ist ein Synchronisationspunkt. Ich würde jedoch nicht erwarten, dass dies in den meisten Szenarien einen großen Unterschied macht.

Im Allgemeinen sollten Sie den einfacheren Stil verwenden, sofern Sie keinen Grund dafür haben. Dann verwenden Sie entweder regelmäßig den const & oder fügen Sie einen Kommentar hinzu, warum Sie ihn nur an wenigen Stellen verwenden.

3
peterchen

Eine Sache, die ich noch nicht gesehen habe, ist, dass, wenn Sie gemeinsam genutzte Zeiger als Referenz übergeben, die implizite Konvertierung verloren geht, die Sie erhalten, wenn Sie einen abgeleiteten gemeinsamen Zeiger über einen Verweis auf einen gemeinsamen Zeiger der Basisklasse übergeben möchten.

Dieser Code führt beispielsweise zu einem Fehler, der jedoch funktioniert, wenn Sie test() so ändern, dass der gemeinsam genutzte Zeiger nicht als Verweis übergeben wird.

#include <boost/shared_ptr.hpp>

class Base { };
class Derived: public Base { };

// ONLY instances of Base can be passed by reference.  If you have a shared_ptr
// to a derived type, you have to cast it manually.  If you remove the reference
// and pass the shared_ptr by value, then the cast is implicit so you don't have
// to worry about it.
void test(boost::shared_ptr<Base>& b)
{
    return;
}

int main(void)
{
    boost::shared_ptr<Derived> d(new Derived);
    test(d);

    // If you want the above call to work with references, you will have to manually cast
    // pointers like this, EVERY time you call the function.  Since you are creating a new
    // shared pointer, you lose the benefit of passing by reference.
    boost::shared_ptr<Base> b = boost::dynamic_pointer_cast<Base>(d);
    test(b);

    return 0;
}
2
Malvineous

Ich gehe davon aus, dass Sie mit vorzeitige Optimierung vertraut sind und dies entweder für akademische Zwecke oder weil Sie bereits vorhandenen Code isoliert haben, der nicht ausreichend ausgeführt wird.

Übergabe als Referenz ist in Ordnung

Das Übergeben von const-Referenzen ist besser und kann normalerweise verwendet werden, da es dem Objekt, auf das verwiesen wird, keine Konstanz aufzwingt.

Sie sind nicht gefährdet, den Zeiger aufgrund der Verwendung einer Referenz zu verlieren. Diese Referenz ist ein Beweis dafür, dass Sie eine Kopie des intelligenten Zeigers im Stapel haben und nur ein Thread einen Aufrufstapel besitzt, sodass eine bereits vorhandene Kopie nicht verschwindet.

Die Verwendung von Referenzen ist häufig aus den von Ihnen genannten Gründen effizienter , jedoch nicht garantiert . Denken Sie daran, dass die Dereferenzierung eines Objekts auch Arbeit kosten kann. Ihr ideales Referenznutzungsszenario wäre, wenn Ihr Codierungsstil viele kleine Funktionen umfasst, bei denen der Zeiger vor der Verwendung von Funktion zu Funktion zu Funktion weitergegeben wird.

Sie sollten immer vermeiden, Ihren Smart Pointer als Referenz zu speichern . Ihr Class::take_copy_of_sp(&sp) Beispiel zeigt die korrekte Verwendung dafür.

1
Drew Dormann

Angenommen, es geht uns nicht um const-Korrektheit (oder mehr, Sie meinen, die Funktionen können die Besitzrechte der übergebenen Daten ändern oder teilen), ist das Übergeben eines boost :: shared_ptr nach Wert sicherer als das Übergeben als Verweis als Wir erlauben dem ursprünglichen boost :: shared_ptr, seine eigene Lebensdauer zu steuern. Betrachten Sie die Ergebnisse des folgenden Codes ...

void FooTakesReference( boost::shared_ptr< int > & ptr )
{
    ptr.reset(); // We reset, and so does sharedA, memory is deleted.
}

void FooTakesValue( boost::shared_ptr< int > ptr )
{
    ptr.reset(); // Our temporary is reset, however sharedB hasn't.
}

void main()
{
    boost::shared_ptr< int > sharedA( new int( 13 ) );
    boost::shared_ptr< int > sharedB( new int( 14 ) );

    FooTakesReference( sharedA );

    FooTakesValue( sharedB );
}

Aus dem obigen Beispiel ist ersichtlich, dass das Übergeben von sharedA by reference FooTakesReference den ursprünglichen Zeiger zurücksetzen kann, wodurch der Verwendungszähler auf 0 reduziert wird, wodurch die Daten zerstört werden. FooTakesValue kann den ursprünglichen Zeiger jedoch nicht zurücksetzen, wodurch sichergestellt wird, dass sharedBs - Daten weiterhin verwendet werden können. Wenn ein anderer Entwickler unweigerlich vorbeikommt und versucht, an sharedA's fragiler Existenz zu huckeln, entsteht Chaos. Der glückliche sharedB - Entwickler geht jedoch früh nach Hause, da in seiner Welt alles stimmt.

Die Codesicherheit wiegt in diesem Fall die Geschwindigkeitsverbesserung beim Kopieren bei weitem. Gleichzeitig soll der boost :: shared_ptr die Codesicherheit verbessern. Es ist viel einfacher, von einer Kopie zu einer Referenz zu wechseln, wenn diese Art von Nischenoptimierung erforderlich ist.

1
Kit10

Ich habe in einem Projekt gearbeitet, bei dem der Grundsatz sehr wichtig war, intelligente Zeiger nach Wert zu übergeben. Als ich gebeten wurde, eine Leistungsanalyse durchzuführen, stellte ich fest, dass die Anwendung für das Inkrementieren und Dekrementieren der Referenzzähler der intelligenten Zeiger 4-6% der verwendeten Prozessorzeit aufwenden muss.

Wenn Sie die intelligenten Zeiger nach Wert weitergeben möchten, um Probleme in seltsamen Fällen, wie von Daniel Earwicker beschrieben, zu vermeiden, stellen Sie sicher, dass Sie den Preis verstehen, den Sie dafür zahlen.

Wenn Sie sich für eine Referenz entscheiden, besteht der Hauptgrund für die Verwendung der const-Referenz darin, das implizite Upcasting zu ermöglichen, wenn Sie einen gemeinsamen Zeiger an ein Objekt von einer Klasse übergeben müssen, die die in der Schnittstelle verwendete Klasse erbt. 

1
gsf

Sandy schrieb: "Es scheint, dass alle Vor-und Nachteile hier auf einen beliebigen Typ verallgemeinert werden können, der durch Verweis übergeben wird, nicht nur durch shared_ptr."

Bis zu einem gewissen Grad ist es richtig, aber der Zweck von shared_ptr besteht darin, Bedenken hinsichtlich der Lebensdauer von Objekten zu beseitigen und dem Compiler zu ermöglichen, dies für Sie zu erledigen. Wenn Sie einen gemeinsam genutzten Zeiger als Referenz übergeben und Clients mit nicht referenzierten Methoden für das Aufrufen des Objekts mit Referenzzählerobjekten erlauben, dass die Objektdaten möglicherweise freigegeben werden, ist die Verwendung eines gemeinsam genutzten Zeigers nahezu sinnlos.

Ich habe in diesem Satz fast "fast" geschrieben, weil Leistung ein Anliegen sein kann und in seltenen Fällen "gerechtfertigt" sein könnte, aber ich würde dieses Szenario auch selbst vermeiden und nach allen möglichen anderen Optimierungslösungen suchen, beispielsweise ernsthaft nachsehen beim Hinzufügen einer weiteren Ebene der Dereferenzierung, faulen Bewertung usw.

Code, der über den Autor hinausgeht oder sogar das Gedächtnis des Autors enthält, erfordert implizite Annahmen über das Verhalten, insbesondere das Verhalten der Objektlebensdauer, erfordert eine klare, präzise und lesbare Dokumentation, und dann lesen viele Clients es sowieso nicht! Einfachheit übertrifft fast immer die Effizienz, und es gibt fast immer andere Möglichkeiten, um effizient zu sein. Wenn Sie wirklich Werte als Referenz übergeben müssen, um zu vermeiden, dass die Konstruktionsobjekte Ihrer Referenzobjekte (und den Gleichheitsoperator) tiefes Kopieren von Kopierkonstruktoren verursachen, sollten Sie möglicherweise überlegen, wie Sie die tief kopierten Daten als Referenzzähler verwenden können schnell kopiert (Natürlich ist dies nur ein Entwurfsszenario, das möglicherweise nicht auf Ihre Situation zutrifft).

1
Bill H

Zusätzlich zu dem, was litb gesagt hat, möchte ich darauf hinweisen, dass es wahrscheinlich im zweiten Beispiel durch const-Referenz passiert wird. Auf diese Weise sind Sie sicher, dass Sie es nicht versehentlich ändern.

0
Leon Timmermans
struct A {
  shared_ptr<Message> msg;
  shared_ptr<Message> * ptr_msg;
}
  1. wert übergeben: 

    void set(shared_ptr<Message> msg) {
      this->msg = msg; /// create a new shared_ptr, reference count will be added;
    } /// out of method, new created shared_ptr will be deleted, of course, reference count also be reduced;
    
  2. als Referenz übergeben:

    void set(shared_ptr<Message>& msg) {
     this->msg = msg; /// reference count will be added, because reference is just an alias.
     }
    
  3. zeiger übergeben:

    void set(shared_ptr<Message>* msg) {
      this->ptr_msg = msg; /// reference count will not be added;
    }
    
0
Dylan Chen

Jedes Codestück muss einen Sinn haben. Wenn Sie in der Anwendung einen gemeinsamen Zeiger nach Wert überall übergeben, bedeutet dies "Ich bin nicht sicher, was anderswo vor sich geht, daher bevorzuge ich die Sicherheit. Ich bezeichne dies nicht als gutes Vertrauenszeichen für andere Programmierer, die den Code lesen könnten.

Selbst wenn eine Funktion eine const-Referenz erhält und Sie "unsicher" sind, können Sie immer noch eine Kopie des gemeinsam genutzten Zeigers am Kopf der Funktion erstellen, um dem Zeiger eine starke Referenz hinzuzufügen. Dies kann auch als Hinweis auf das Design verstanden werden ("der Zeiger kann an anderer Stelle geändert werden").

Also ja, IMO, der Standard sollte " Pass by const reference " sein.

0
Philippe