Program pendidikan untuk melewatkan parameter berdasarkan nilai ke konstruktor dan setter (C ++ modern, contoh)

Dilihat oleh komentar habr.com/en/post/460831/#comment_20416435 di pos berikutnya dan diskusi yang terbuka di sana, artikel tentang cara menyampaikan argumen dengan benar ke konstruktor atau setter tidak akan menghalangi HabrΓ©. StackOverflow memiliki banyak materi serupa, tetapi saya tidak ingat sesuatu di sini.

Karena contoh dalam artikel itu sepenuhnya benar, dan penulis artikel itu benar sekali. Berikut ini sebuah contoh:

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

Kode tersebut memungkinkan Anda untuk mencakup semua (well, hampir semua) opsi yang mungkin untuk menggunakan kelas:

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

Bandingkan dengan metode lama, ketika dilewatkan oleh const &: sudah pasti lebih buruk, karena tidak termasuk opsi (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& . 

Alternatif dengan && juga lebih buruk, tetapi berlawanan arah:

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

Jika Anda tidak takut dengan ledakan kombinatorial, Anda dapat memberikan kesempatan untuk && (tapi mengapa? Tidak akan ada peningkatan kecepatan, pengoptimal tidak tertidur):

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

Atau hal yang sama, hanya dengan templat (tapi sekali lagi, mengapa?):

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

Bahkan jika Anda tidak memiliki std :: string, tetapi beberapa objek dari kelas besar Anda sendiri, dan Anda ingin orang membuatnya bergerak (dan tidak menyalin), maka dalam hal ini lebih baik untuk melarang konstruktor untuk menyalin dari kelas besar ini daripada meneruskannya ke mana-mana oleh &&. Ini lebih dapat diandalkan, dan kode lebih pendek.

Akhirnya, beberapa opsi tentang cara TIDAK LAYAK:

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

Dan jangan:

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

Mengapa ini terjadi, apa prinsip dasarnya? Ini sederhana: sebuah objek, sebagai aturan, harus SENDIRI sifatnya.

Jika objek tidak ingin memiliki sesuatu, maka itu dapat memiliki shared_ptr untuk "sesuatu" ini. Ngomong-ngomong, dalam hal ini shared_ptr juga harus diteruskan oleh nilai, dan bukan oleh tautan konstan - tidak ada perbedaan dengan contoh pertama di awal artikel:

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

Harap dicatat: std :: move untuk shared_ptr benar-benar legal, ini menghilangkan overhead dari penguncian penghitung tautan shared_ptr dalam memori (ratusan siklus CPU) dan kenaikannya. Itu tidak mempengaruhi masa objek dan tautan lain ke sana. Tetapi (X) dapat dilakukan, tentu saja, hanya jika tautan psh dalam kode di bawah ini tidak lagi diperlukan.

Moral: jangan gunakan const & secara umum. Terlihat sesuai dengan keadaan.

PS
Gunakan {} alih-alih () saat meneruskan parameter konstruktor. Fashionable, modern, awet muda.

PPS
Sebagai kesimpulan, satu hal lagi: std :: move () sebenarnya tidak memindahkan apa pun dan dengan sendirinya diterjemahkan menjadi instruksi assembler nol. Semua yang std :: move () lakukan adalah meletakkan "sticky label" khusus pada tautan, mengubahnya menjadi referensi nilai&& -. Dan lebih lanjut adalah mungkin dengan label ini untuk secara terpisah "mencocokkan" jenis parameter fungsi (misalnya, untuk memiliki kelebihan fungsi terpisah untuk parameter && - dan parameter terpisah untuk & -parameter). Arti dari label && - adalah untuk mengaktifkan kode panggilan untuk memberi tahu yang dipanggil: β€œjika Anda mau, Anda bisa makan nilainya dari tautan ini, saya tidak lagi membutuhkannya; tetapi hanya jika Anda makan, tinggalkan tulangnya, maka saya masih perlu memanggil destruktor untuk kerangka yang tersisa. " Dengan kesuksesan yang sama, akan mungkin untuk mengirimkan tautan & biasa (Anda juga dapat menggunakan objek untuk "memakannya"), tetapi dengan && semantik lebih baik, karena Anda tidak akan bingung: di mana Anda bisa makan, dan di mana Anda hanya bisa mencium.

Dalam hal ini, nama std :: move () harus dikenali sebagai sangat tidak berhasil. Akan benar untuk menyebutnya std :: eat_me_if_you_want () atau std :: bon_appetit (). Tetapi std :: move () lebih pendek.

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


All Articles