برنامج تعليمي لتمرير المعلمات حسب القيمة للبنائين والمستوطنين (C ++ الحديثة ، أمثلة)

استنادا إلى التعليقات habr.com/en/post/460831/#comment_20416435 في المنشور التالي والمناقشة التي بدأت هناك ، فإن المقالة حول كيفية تمرير الحجج بشكل صحيح إلى المُنشئ أو المقيد لن تعيق Habré. يحتوي StackOverflow على الكثير من المواد المماثلة ، لكنني لا أتذكر شيئًا هنا.

لأن المثال في هذه المقالة صحيح تمامًا ، ومؤلف المقال صحيح تمامًا. هنا مثال:

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

يسمح لك هذا الرمز بتغطية جميع الخيارات الممكنة (جيدًا تقريبًا) لاستخدام الفئة:

 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"}; //   

قارن مع الطريقة القديمة ، عندما يتم تمريرها بواسطة const &: إنها بالتأكيد أسوأ ، لأنها تستبعد الخيار (3):

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

البديل مع && هو أيضًا أسوأ ، ولكن في الاتجاه المعاكس:

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

إذا لم تكن خائفًا من حدوث انفجار اندماجي ، فيمكنك إعطاء فرصة لـ && (لكن لماذا؟ لن يكون هناك مكسب حقيقي في السرعة ، لا يتوقف المحسن عن التجميد):

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

أو نفس الشيء ، فقط مع القوالب (ولكن مرة أخرى ، لماذا؟):

 //     (   ),      . 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; }; 

حتى لو لم يكن لديك std :: string ، لكن بعض العناصر من صفك الكبير المكتوب ، وتريد أن ينقلها الناس (وليس نسخًا) ، فمن الأفضل في هذه الحالة منع المُنشئ من النسخ من هذه الفئة الكبيرة بدلاً من تمريرها في كل مكان بواسطة &&. هذا أكثر موثوقية ، والكود أقصر.

أخيرًا ، هناك خياران حول كيفية القيام بما يلي:

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

وهكذا لا:

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

لماذا يحدث هذا ، ما هو المبدأ الأساسي؟ إنها بسيطة: كقاعدة ، يجب أن يمتلك خصائصه.

إذا كان الكائن لا يريد امتلاك شيء ما ، فيمكنه عندئذٍ امتلاك Shared_ptr لهذا "الشيء". بالمناسبة ، في هذه الحالة ، يجب أيضًا تمرير_تحديد_المشترك بالقيمة ، وليس بواسطة رابط ثابت - لا يوجد فرق مع المثال الأول في بداية المقال:

 //  (  ). 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    

يرجى ملاحظة أن: std :: move for shared_ptr قانوني تمامًا ، فهو يزيل الحمل الزائد لقفل عداد ارتباط Shared_ptr في الذاكرة (مئات دورات وحدة المعالجة المركزية) وزيادة هذه البيانات. لا يؤثر على عمر الكائن والروابط الأخرى به. لكن (X) يمكن القيام به ، بالطبع ، فقط إذا لم تعد هناك حاجة إلى الرابط psh في الكود أدناه.

الأخلاقية: لا تستخدم const & بشكل عام. انظر وفقا للظروف.

PS
استخدم {} بدلاً من () عند تمرير معلمات المنشئ. عصري ، شبابي.

PPS
في الختام ، هناك شيء آخر: std :: move () في الواقع لا يحرك أي شيء ويترجم في حد ذاته إلى تعليمات المجمّع صفر. كل ما تفعله std :: move () هو وضع "ملصق لاصق" خاص على الرابط ، وتحويله إلى مرجع && - rvalue. علاوة على ذلك ، من الممكن مع هذه التسمية أن "تقوم" بمطابقة نوع معلمة الوظيفة بشكل منفصل (على سبيل المثال ، أن يكون هناك حمل زائد لوحدة منفصلة للمعلمة && - ومنفصل لـ & -المعلمة). معنى العلامة && - هو تمكين رمز الاتصال لإخبار ما يسمى: "إذا أردت ، يمكنك أن تأكل القيمة من هذا الرابط ، لم أعد بحاجة إليها ؛ ولكن فقط إذا كنت تأكل ، اترك العظام ، فأنا لا أزال بحاجة إلى استدعاء المدمر للهيكل العظمي المتبقي ". مع نفس النجاح ، سيكون من الممكن نقل الارتباطات العادية & -links (يمكنك أيضًا استخدام الكائن "للأكل") ، ولكن مع && دلالات ، لأن لن تخلط بين: حيث يمكنك أن تأكل ، وأين يمكنك أن تشتم فقط.

في هذا الصدد ، يجب التعرف على الاسم std :: move () على أنه غير ناجح للغاية. سيكون من الصحيح أن نسميها std :: eat_me_if_you_want () أو std :: bon_appetit (). لكن الأمراض المنقولة جنسيا :: move () أقصر.

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


All Articles