Dalam C ++ tradisional, meneruskan nilai ke fungsi dan metode lambat untuk objek besar, dan umumnya disukai. Alih-alih, programmer C ++ cenderung meneruskan referensi, yang lebih cepat, tetapi yang memperkenalkan segala macam pertanyaan rumit seputar kepemilikan dan terutama seputar manajemen memori (jika objek dialokasikan dialokasikan)
Sekarang, di C ++ 11, kami memiliki referensi nilai dan memindahkan konstruktor, yang berarti bahwa mungkin untuk mengimplementasikan objek besar (seperti std::vector
) yang murah untuk melewati nilai ke dalam dan keluar dari suatu fungsi.
Jadi, apakah ini berarti bahwa standarnya harus lewat nilai untuk contoh jenis seperti std::vector
dan std::string
? Bagaimana dengan objek kustom? Apa praktik terbaik yang baru?
sumber
pass by reference ... which introduces all sorts of complicated questions around ownership and especially around memory management (in the event that the object is heap-allocated)
. Saya tidak mengerti bagaimana rumit atau bermasalah untuk kepemilikan? Mungkin saya melewatkan sesuatu?const std::string&
dan bukan salinan. Utas pertama kemudian keluar ...Jawaban:
Ini adalah standar yang masuk akal jika Anda perlu membuat salinan di dalam tubuh. Ini adalah apa Dave Abrahams menganjurkan :
Dalam kode ini berarti jangan lakukan ini:
tetapi lakukan ini:
yang memiliki keuntungan yang bisa digunakan oleh penelepon
foo
seperti:dan hanya sedikit pekerjaan yang dilakukan. Anda perlu dua kelebihan untuk melakukan hal yang sama dengan referensi,
void foo(T const&);
danvoid foo(T&&);
.Dengan mengingat hal itu, saya sekarang menulis konstruktor berharga saya seperti:
Kalau tidak, melewati referensi
const
masih masuk akal.sumber
SomeProperty p;
for (auto x: vec) { x.foo(p); }
tidak cocok, misalnya. Juga, Pindah Konstruktor memiliki biaya (semakin besar objek, semakin tinggi biaya) sementaraconst&
pada dasarnya gratis.std::vector
dengan sejuta elemen berharga sama dengan memindahkan satu dengan lima elemen karena hanya penunjuk ke array pada tumpukan yang dipindahkan, tidak setiap objek dalam vektor. Jadi sebenarnya bukan masalah besar.std::move
semua tempat ..const&
, yang telah membuat saya tersandung beberapa kali.void foo(const T&); int main() { S s; foo(s); }
. Ini dapat dikompilasi, meskipun jenisnya berbeda, jika ada konstruktor T yang menggunakan S sebagai argumen. Ini bisa lambat, karena objek T besar mungkin sedang dibangun. Anda mungkin berpikir Anda melewatkan referensi tanpa menyalin, tetapi mungkin Anda memang demikian. Lihat jawaban ini untuk pertanyaan yang saya minta lebih banyak. Pada dasarnya,&
biasanya hanya mengikat nilai-nilai, tetapi ada pengecualian untukrvalue
. Ada beberapa alternatif.Dalam hampir semua kasus, semantik Anda harus berupa:
Semua tanda tangan lainnya harus digunakan hanya dengan hemat, dan dengan justifikasi yang baik. Kompiler sekarang akan selalu bekerja dengan cara yang paling efisien. Anda bisa terus menulis kode Anda!
sumber
foo(bar& x) { x.a = 3; }
Heck of a jauh lebih dapat diandalkan (dan dapat dibaca!)foo(bar* x) {if (!x) throw std::invalid_argument("x"); x->a = 3;
ref
kata kunci dalam C #).is shared_ptr intended to never be null? Much as (I think) unique_ptr is?
Kedua asumsi itu salah.unique_ptr
danshared_ptr
dapat menyimpan null /nullptr
nilai. Jika Anda tidak ingin khawatir tentang nilai nol, Anda harus menggunakan referensi, karena mereka tidak akan pernah menjadi nol. Anda juga tidak perlu mengetik->
, yang menurut Anda menyebalkan :)Lewati parameter dengan nilai jika di dalam fungsi tubuh Anda memerlukan salinan objek atau hanya perlu memindahkan objek. Lewati
const&
jika Anda hanya perlu akses non-mutasi ke objek.Contoh copy objek:
Contoh pemindahan objek:
Contoh akses non-mutasi:
Untuk alasannya, lihat posting blog ini oleh Dave Abrahams dan Xiang Fan .
sumber
Tanda tangan suatu fungsi harus mencerminkan penggunaan yang dimaksudkan. Keterbacaan penting, juga untuk pengoptimal.
Ini adalah prasyarat terbaik bagi pengoptimal untuk membuat kode tercepat - setidaknya dalam teori dan jika tidak dalam kenyataan maka dalam beberapa tahun kenyataan.
Pertimbangan kinerja seringkali dinilai berlebihan dalam konteks parameter yang lewat. Penerusan yang sempurna adalah contohnya. Fungsi seperti
emplace_back
kebanyakan sangat pendek dan inline.sumber