Apa manfaat lewat pointer dari melewati referensi di C ++?
Akhir-akhir ini, saya telah melihat sejumlah contoh yang memilih lewat argumen fungsi oleh pointer daripada lewat referensi. Apakah ada manfaatnya melakukan ini?
Contoh:
func(SPRITE *x);
dengan panggilan
func(&mySprite);
vs.
func(SPRITE &x);
dengan panggilan
func(mySprite);
c++
pointers
parameter-passing
pass-by-reference
Matt Pascoe
sumber
sumber
new
untuk membuat pointer dan masalah kepemilikan yang dihasilkan.Jawaban:
Pointer dapat menerima parameter NULL, parameter referensi tidak bisa. Jika ada kemungkinan Anda ingin meneruskan "tidak ada objek", maka gunakan pointer daripada referensi.
Selain itu, lewat penunjuk memungkinkan Anda melihat secara eksplisit di situs panggilan apakah objek dilewatkan oleh nilai atau dengan referensi:
sumber
func2 passes by reference
. Sementara saya menghargai bahwa Anda bermaksud melewati "dengan referensi" dari perspektif level tinggi, diimplementasikan dengan melewatkan pointer pada perspektif level kode, ini sangat membingungkan (lihat stackoverflow.com/questions/13382356/… ).func(int& a)
tidak valid C dalam versi standar apa pun. Anda mungkin mengkompilasi file Anda sebagai C ++ secara tidak sengaja.Melewati dengan pointer
nothing
. Ini dapat digunakan untuk memberikan argumen opsional.Lewati dengan referensi
string s = &str1 + &str2;
menggunakan pointer.void f(const T& t); ... f(T(a, b, c));
:, pointer tidak dapat digunakan seperti itu karena Anda tidak dapat mengambil alamat sementara.sumber
Saya suka alasannya dengan artikel dari "cplusplus.com:"
Apa yang saya ambil dari ini adalah bahwa perbedaan utama antara memilih untuk menggunakan pointer atau parameter referensi adalah jika NULL adalah nilai yang dapat diterima. Itu dia.
Apakah nilainya input, output, dapat dimodifikasi dll. Harus dalam dokumentasi / komentar tentang fungsi, setelah semua.
sumber
Allen Holub "Cukup Rope untuk Menembak Diri Anda di Kaki" daftar 2 aturan berikut:
Dia mencantumkan beberapa alasan mengapa referensi ditambahkan ke C ++:
const
referensi memungkinkan Anda memiliki semantik nilai per nilai sambil menghindari salinanPoin utamanya adalah bahwa referensi tidak boleh digunakan sebagai parameter 'output' karena di situs panggilan tidak ada indikasi apakah parameter tersebut merupakan referensi atau parameter nilai. Jadi aturannya adalah hanya menggunakan
const
referensi sebagai argumen.Secara pribadi, saya pikir ini adalah aturan praktis yang baik karena membuatnya lebih jelas ketika suatu parameter adalah parameter keluaran atau tidak. Namun, sementara saya pribadi setuju dengan ini secara umum, saya membiarkan diri saya terpengaruh oleh pendapat orang lain di tim saya jika mereka memperdebatkan parameter output sebagai referensi (beberapa pengembang sangat menyukainya).
sumber
Klarifikasi ke posting sebelumnya:
Referensi BUKAN jaminan mendapatkan pointer non-null. (Meskipun kita sering memperlakukan mereka seperti itu.)
Walaupun kode tersebut sangat buruk, seperti saat membawa Anda keluar di balik kode buruk woodshed , yang berikut ini akan dikompilasi & dijalankan: (Setidaknya di bawah kompiler saya.)
Masalah sebenarnya yang saya miliki dengan referensi ada pada programmer lain, untuk selanjutnya disebut IDIOTS , yang mengalokasikan dalam konstruktor, membatalkan alokasi dalam destruktor, dan gagal memasok copy constructor atau operator = ().
Tiba-tiba ada perbedaan antara foo (BAR bar) dan foo (BAR & bar) . (Operasi salin bitwise otomatis dipanggil. Deallokasi di destructor dipanggil dua kali.)
Untungnya kompiler modern akan mengambil alokasi ganda ini dari pointer yang sama. 15 tahun yang lalu, mereka tidak melakukannya. (Di bawah gcc / g ++, gunakan setenv MALLOC_CHECK_ 0 untuk meninjau kembali cara lama.) Menghasilkan, di bawah DEC UNIX, dalam memori yang sama dialokasikan ke dua objek yang berbeda. Banyak kesenangan debugging di sana ...
Lebih praktis:
sumber
*i
, program Anda memiliki perilaku yang tidak jelas. Sebagai contoh, kompiler dapat melihat kode ini dan menganggap "OK, kode ini memiliki perilaku yang tidak terdefinisi di semua jalur kode, sehingga seluruh fungsi ini tidak dapat dijangkau." Maka akan diasumsikan bahwa semua cabang yang mengarah ke fungsi ini tidak diambil. Ini adalah optimasi yang dilakukan secara rutin.Sebagian besar jawaban di sini gagal mengatasi ambiguitas yang melekat dalam memiliki pointer mentah dalam tanda tangan fungsi, dalam hal mengekspresikan niat. Masalahnya adalah sebagai berikut:
Penelepon tidak tahu apakah penunjuk menunjuk ke objek tunggal, atau ke awal "array" objek.
Penelepon tidak tahu apakah pointer "memiliki" memori yang ditunjuknya. Yaitu apakah fungsi tersebut harus membebaskan memori atau tidak. (
foo(new int)
- Apakah ini kebocoran memori?).Penelepon tidak tahu apakah
nullptr
bisa dengan aman masuk ke dalam fungsi.Semua masalah ini diselesaikan dengan referensi:
Referensi selalu merujuk ke satu objek.
Referensi tidak pernah memiliki ingatan yang mereka rujuk, mereka hanyalah pandangan ke ingatan.
Referensi tidak boleh nol.
Ini menjadikan referensi kandidat yang jauh lebih baik untuk penggunaan umum. Namun, referensi tidak sempurna - ada beberapa masalah besar yang perlu dipertimbangkan.
&
operator untuk menunjukkan bahwa kita memang melewati pointer. Sebagai contoh,int a = 5; foo(a);
Tidak jelas sama sekali di sini bahwa a sedang disahkan oleh referensi dan dapat dimodifikasi.std::optional<T&>
tidak valid (untuk alasan yang baik), petunjuk memberi kami nullability yang Anda inginkan.Jadi sepertinya ketika kita menginginkan referensi yang dapat dibatalkan dengan tipuan eksplisit, kita harus meraih
T*
hak? Salah!Abstraksi
Dalam keputus-asaan kita untuk nullability, kita dapat meraih
T*
, dan mengabaikan semua kekurangan dan ambiguitas semantik yang disebutkan sebelumnya. Sebagai gantinya, kita harus meraih apa yang paling baik dilakukan C ++: abstraksi. Jika kita hanya menulis kelas yang membungkus sebuah pointer, kita memperoleh ekspresif, serta nullability dan tipuan eksplisit.Ini adalah antarmuka paling sederhana yang bisa saya buat, tapi itu berfungsi dengan efektif. Ini memungkinkan untuk menginisialisasi referensi, memeriksa apakah suatu nilai ada dan mengakses nilai tersebut. Kita bisa menggunakannya seperti ini:
Kami telah mencapai tujuan kami! Sekarang mari kita lihat manfaatnya, dibandingkan dengan pointer mentah.
nullptr
dapat diteruskan, karena fungsi penulis secara eksplisit memintaoptional_ref
Kita bisa membuat antarmuka lebih kompleks dari sini, seperti menambahkan operator kesetaraan, monadik
get_or
danmap
antarmuka, metode yang mendapatkan nilai atau melempar pengecualian,constexpr
dukungan. Itu bisa dilakukan oleh Anda.Sebagai kesimpulan, alih-alih menggunakan pointer mentah, alasan tentang apa yang sebenarnya dimaksud pointer dalam kode Anda, dan baik memanfaatkan abstraksi perpustakaan standar atau menulis milik Anda. Ini akan meningkatkan kode Anda secara signifikan.
sumber
Tidak juga. Secara internal, melewati dengan referensi dilakukan dengan dasarnya melewati alamat objek yang direferensikan. Jadi, sebenarnya tidak ada keuntungan efisiensi yang bisa didapat dengan melewatkan sebuah pointer.
Melewati dengan referensi memang memiliki satu manfaat. Anda dijamin memiliki instance dari objek / tipe apa pun yang sedang dilewati. Jika Anda meneruskan sebuah pointer, maka Anda berisiko mengambil pointer NULL. Dengan menggunakan pass-by-reference, Anda mendorong pemeriksaan NULL implisit satu tingkat ke pemanggil fungsi Anda.
sumber