Bildungsprogramm zur Übergabe von Parametern nach Wert an Konstruktoren und Setter (modernes C ++, Beispiele)

Gemessen an den Kommentaren habr.com/en/post/460831/#comment_20416435 im nächsten Beitrag und der dort stattfindenden Diskussion wird der Artikel über die korrekte Übergabe von Argumenten an den Konstruktor oder Setter Habré nicht behindern. StackOverflow hat viel ähnliches Material, aber ich kann mich hier an nichts erinnern.

Weil das Beispiel in diesem Artikel völlig korrekt ist und der Autor des Artikels absolut richtig ist. Hier ist ein Beispiel:

// . struct person { person(std::string first_name, std::string last_name) : first_name{std::move(first_name)} //  , last_name{std::move(last_name)} // std::move  ! {} private: std::string first_name; std::string last_name; }; 

Mit einem solchen Code können Sie alle (fast alle) möglichen Optionen für die Verwendung der Klasse abdecken:

 std::string first{"abc"}, last{"def"}; person p1{first, last}; // (1)    person p2{std::move(first), last}; // !!!    person p2{std::move(first), std::move(last)}; // (3)   person p3{"x", "y"}; //   

Vergleichen Sie mit der alten Methode, wenn sie von const & übergeben wird: es ist definitiv schlimmer, weil es Option (3) ausschließt:

 // . struct person { person(const std::string& first_name, const std::string& last_name) : first_name{first_name} , last_name{last_name} {} private: std::string first_name; std::string last_name; }; std::string first{"abc"}, last{"def"}; person p1{first, last}; //  ,    //      ,  first  last    // ?         //   0 !  const& . 

Eine Alternative mit && ist auch schlechter, aber in die entgegengesetzte Richtung:

 // . struct person { person(std::string&& first_name, std::string&& last_name) : first_name{std::move(first_name)} , last_name{std::move(last_name)} {} private: std::string first_name; std::string last_name; }; std::string first{"abc"}, last{"def"}; person p1{std::move(first), std::move(last)}; //  //       ,    &&  : person p2{std::string{first}, std::string{last}}; // FOOOO 

Wenn Sie keine Angst vor einer kombinatorischen Explosion haben, können Sie && eine Chance geben (aber warum? Es wird keinen wirklichen Geschwindigkeitsgewinn geben, der Optimierer döst nicht ein):

 //   . struct person { person(std::string&& first_name, std::string&& last_name) : first_name{std::move(first_name)} , last_name{std::move(last_name)} {} person(const std::string& first_name, std::string&& last_name) : first_name{first_name} , last_name{std::move(last_name)} {} person(std::string&& first_name, const std::string& last_name) : first_name{std::move(first_name)} , last_name{last_name} {} person(const std::string& first_name, const std::string& last_name) : first_name{first_name} , last_name{last_name} {} private: std::string first_name; std::string last_name; }; 

Oder das Gleiche, nur mit Vorlagen (aber nochmal, warum?):

 //     (   ),      . struct person { template <typename T1, typename T2> person(T1&& first_name, T2&& last_name) : first_name{std::forward<T1>(first_name)} , last_name{std::forward<T2>(last_name)} {} private: std::string first_name; std::string last_name; }; 

Selbst wenn Sie nicht über std :: string verfügen, sondern über ein Objekt Ihrer eigenen großen Klasse, und Sie möchten, dass die Benutzer es verschieben (und nicht kopieren), ist es in diesem Fall besser, dem Konstruktor das Kopieren aus dieser großen Klasse zu verbieten, als es überall zu übergeben von &&. Dies ist zuverlässiger und der Code ist kürzer.

Zum Schluss noch ein paar Möglichkeiten, wie man NICHT WERT macht:

 // . struct person { person(const std::string& first_name, const std::string& last_name) : first_name{first_name} , last_name{last_name} {} private: //   :  ,     //     const std::string& first_name; const std::string& last_name; }; person p{"x", "y"}; // --,  

Und so nicht:

 // . struct person { person(std::string& first_name, std::string& last_name) : first_name{first_name} , last_name{last_name} {} private: //   ,      shared_ptr: //  ,   std::string& first_name; std::string& last_name; }; 

Warum passiert das, was ist das Grundprinzip? Es ist einfach: Ein Objekt sollte in der Regel seine Eigenschaften besitzen.

Wenn das Objekt nichts besitzen möchte, kann es shared_ptr für dieses „Etwas“ besitzen. Übrigens sollte in diesem Fall shared_ptr auch als Wert und nicht als konstante Verknüpfung übergeben werden - es gibt keinen Unterschied zum allerersten Beispiel am Anfang des Artikels:

 //  (  ). struct person { person(std::shared_ptr<portfolio> pf) : pf{std::move(pf)} // std::move     {} private: std::shared_ptr<portfolio> pf; }; auto psh = std::make_shared<portfolio>("xxx", "yyy", "zzz"); ... person p1{psh}; person p2{std::move(psh)}; // (X)  ,  psh    

Bitte beachten Sie: std :: move für shared_ptr ist völlig legal. Es beseitigt den Aufwand für das Sperren des Shared_ptr-Verbindungszählers im Speicher (Hunderte von CPU-Zyklen) und dessen Inkrement. Dies hat keinen Einfluss auf die Lebensdauer des Objekts und anderer Links zu diesem Objekt. Aber (X) kann natürlich nur durchgeführt werden, wenn der psh-Link im folgenden Code nicht mehr benötigt wird.

Moral: Verwenden Sie const & im Allgemeinen nicht. Schauen Sie nach den Umständen.

PS
Verwenden Sie {} anstelle von (), wenn Sie Konstruktorparameter übergeben. Modisch, modern, jugendlich.

PPS
Abschließend noch eine Sache: std :: move () verschiebt tatsächlich nichts und übersetzt sich an sich in Null-Assembler-Anweisungen. Alles, was std :: move () tut, ist, ein spezielles „Sticky Label“ auf den Link zu setzen und ihn in eine && - rWert-Referenz zu verwandeln. Außerdem ist es mit dieser Bezeichnung möglich, den Typ des Funktionsparameters separat „abzugleichen“ (z. B. eine separate Funktionsüberladung für den Parameter && - und eine separate für den Parameter &&). Die Bedeutung des && - Labels besteht darin, dass der aufrufende Code dem Angerufenen mitteilt: „Wenn Sie möchten, können Sie den Wert über diesen Link essen, ich brauche ihn nicht mehr. aber nur wenn du isst, lass die Knochen, dann muss ich immer noch den Zerstörer für das verbleibende Skelett anrufen. " Mit dem gleichen Erfolg wäre es möglich, gewöhnliche & -Links zu übertragen (Sie können auch ein Objekt mit ihnen "essen"), aber mit && ist die Semantik besser, weil Sie werden nicht verwirren: wo Sie essen können und wo Sie nur riechen können.

In dieser Hinsicht sollte der Name std :: move () als äußerst erfolglos erkannt werden. Es wäre richtig, es std :: eat_me_if_you_want () oder std :: bon_appetit () zu nennen. Aber std :: move () ist kürzer.

Source: https://habr.com/ru/post/de460955/


All Articles