Saya memahami sintaks dan semantik umum dari pointer versus referensi, tetapi bagaimana saya harus memutuskan kapan lebih tepat atau kurang tepat untuk menggunakan referensi atau pointer dalam API?
Secara alami beberapa situasi memerlukan satu atau yang lain ( operator++
perlu argumen referensi), tetapi secara umum saya menemukan saya lebih suka menggunakan pointer (dan pointer const) karena sintaksnya jelas bahwa variabel sedang dilewatkan secara destruktif.
Misalnya dalam kode berikut:
void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
int a = 0;
add_one(a); // Not clear that a may be modified
add_one(&a); // 'a' is clearly being passed destructively
}
Dengan pointer, selalu (lebih) jelas apa yang terjadi, jadi untuk API dan sejenisnya di mana kejelasan adalah masalah besar, pointer tidak lebih tepat daripada referensi? Apakah itu berarti referensi hanya boleh digunakan bila perlu (misalnya operator++
)? Apakah ada masalah kinerja dengan satu atau yang lain?
EDIT (LUAR BIASA):
Selain memungkinkan nilai NULL dan berurusan dengan array mentah, tampaknya pilihannya adalah preferensi pribadi. Saya telah menerima jawaban di bawah ini yang merujuk pada Panduan Gaya C ++ Google , karena mereka menyajikan pandangan bahwa "Referensi dapat membingungkan, karena mereka memiliki sintaks nilai tetapi semantik penunjuk.".
Karena pekerjaan tambahan yang diperlukan untuk membersihkan argumen pointer yang tidak boleh NULL (misalnya add_one(0)
akan memanggil versi pointer dan istirahat selama runtime), masuk akal dari perspektif rawatan untuk menggunakan referensi di mana objek HARUS hadir, meskipun itu memalukan kehilangan kejelasan sintaksis.
add_one(a);
tidak jelas apa yanga
akan dimodifikasi? Dikatakan tepat di kode: tambahkan satu .addOneTo(...)
. Jika bukan itu yang ingin Anda lakukan, lihat saja deklarasi tersebut.Jawaban:
Gunakan referensi di mana pun Anda bisa, petunjuk mana pun Anda harus.
Hindari petunjuk sampai Anda tidak bisa.
Alasannya adalah bahwa pointer membuat hal-hal lebih sulit untuk diikuti / dibaca, manipulasi kurang aman dan jauh lebih berbahaya daripada konstruksi lainnya.
Jadi aturan praktisnya adalah menggunakan pointer hanya jika tidak ada pilihan lain.
Sebagai contoh, mengembalikan pointer ke objek adalah opsi yang valid ketika fungsi dapat mengembalikan nullptr dalam beberapa kasus dan diasumsikan akan. Yang mengatakan, pilihan yang lebih baik adalah menggunakan sesuatu yang mirip
boost::optional
.Contoh lain adalah menggunakan pointer ke memori mentah untuk manipulasi memori tertentu. Itu harus disembunyikan dan dilokalkan di bagian kode yang sangat sempit, untuk membantu membatasi bagian berbahaya dari seluruh basis kode.
Dalam contoh Anda, tidak ada gunanya menggunakan pointer sebagai argumen karena:
nullptr
argumen, Anda akan berada di tanah perilaku yang tidak terdefinisi;Jika perilaku fungsi harus bekerja dengan atau tanpa objek yang diberikan, maka menggunakan pointer sebagai atribut menunjukkan bahwa Anda dapat lulus
nullptr
sebagai argumen dan tidak masalah untuk fungsi. Itu semacam kontrak antara pengguna dan implementasi.sumber
add_one(a)
tidak mengembalikan hasilnya, daripada mengaturnya dengan referensi?add_one(a)
membingungkan, maka itu karena namanya tidak benar.add_one(&a)
akan memiliki kebingungan yang sama, hanya sekarang Anda mungkin menambah pointer dan bukan objek.add_one_inplace(a)
akan menghindari semua kebingungan.NULL
dannullptr
, dan itu memiliki mereka karena suatu alasan. Dan itu bukan saran yang dipertimbangkan dengan baik atau bahkan realistis untuk memberikan "tidak pernah menggunakan pointer", dan / atau "tidak pernah menggunakan NULL, selalu gunakanboost::optional
". Itu gila. Jangan salah paham, pointer mentah lebih jarang dibutuhkan dalam C ++ daripada di C, tapi tetap saja, mereka berguna, mereka tidak "berbahaya" seperti yang beberapa orang C ++ suka klaim (itu juga berlebihan), dan lagi: ketika lebih mudah menggunakan pointer danreturn nullptr;
menunjukkan nilai yang hilang ... Mengapa mengimpor seluruh Boost?if
apakah sudah usang dan orang harus menggunakannyawhile() { break; }
, kan? Juga, jangan khawatir, saya telah melihat dan bekerja dengan basis kode yang besar, dan ya, jika Anda ceroboh , maka kepemilikan adalah masalah. Tidak jika Anda tetap pada konvensi, gunakan secara konsisten dan komentar serta dokumentasikan kode Anda. Tapi bagaimanapun, saya harus menggunakan C karena saya terlalu bodoh untuk C ++, kan?Pertunjukannya persis sama, karena referensi diterapkan secara internal sebagai petunjuk. Dengan demikian Anda tidak perlu khawatir tentang hal itu.
Tidak ada konvensi yang diterima secara umum mengenai kapan harus menggunakan referensi dan petunjuk. Dalam beberapa kasus Anda harus mengembalikan atau menerima referensi (copy constructor, misalnya), tetapi selain itu Anda bebas untuk melakukan apa yang Anda inginkan. Sebuah konvensi yang agak umum saya temui adalah menggunakan referensi ketika parameter harus merujuk objek yang ada dan pointer ketika nilai NULL ok.
Beberapa konvensi pengkodean (seperti Google ) menetapkan bahwa seseorang harus selalu menggunakan pointer, atau referensi const, karena referensi memiliki sedikit sintaksis yang tidak jelas: mereka memiliki perilaku referensi tetapi menghargai sintaksis nilai.
sumber
add_one
adalah rusak :add_one(0); // passing a perfectly valid pointer value
, kaboom. Anda perlu memeriksa apakah itu nol. Beberapa orang akan membalas: "baik saya hanya akan mendokumentasikan bahwa fungsi saya tidak bekerja dengan nol". Tidak apa-apa, tetapi kemudian Anda mengalahkan tujuan pertanyaan: jika Anda akan melihat ke dokumentasi untuk melihat apakah nol tidak apa-apa, Anda juga akan melihat deklarasi fungsi .int& i = *((int*)0);
Ini bukan jawaban yang valid. Masalah dalam kode sebelumnya terletak pada penggunaan pointer, bukan dengan referensi . Referensi tidak pernah nol, titik.Dari C ++ FAQ Lite -
sumber
C with class
(yang bukan C ++).Aturan praktis saya adalah:
&
)const
jika ini merupakan parameter yang masuk)const T&
).int ¤t = someArray[i]
)Apa pun yang Anda gunakan, jangan lupa untuk mendokumentasikan fungsi Anda dan arti parameternya jika tidak jelas.
sumber
Penafian: selain fakta bahwa referensi tidak boleh NULL atau "melambung" (artinya mereka tidak dapat mengubah objek tempat mereka alias), itu benar-benar mengarah ke masalah selera, jadi saya tidak akan mengatakan "ini lebih baik".
Yang mengatakan, saya tidak setuju dengan pernyataan terakhir Anda di pos, karena saya tidak berpikir kode kehilangan kejelasan dengan referensi. Dalam contoh Anda,
mungkin lebih jelas dari
karena Anda tahu bahwa kemungkinan besar nilai a akan berubah. Namun di sisi lain, tanda tangan dari fungsi
agak tidak jelas: apakah n akan menjadi bilangan bulat tunggal atau array? Terkadang Anda hanya memiliki akses ke tajuk (yang tidak didokumentasikan), dan tanda tangan seperti
tidak mudah untuk ditafsirkan pada pandangan pertama.
Imho, referensi sama baiknya dengan pointer ketika tidak ada alokasi (kembali) atau rebinding (dalam arti dijelaskan sebelumnya) diperlukan. Selain itu, jika pengembang hanya menggunakan pointer untuk array, tanda tangan fungsi agak kurang ambigu. Belum lagi fakta bahwa sintaksis operator jauh lebih mudah dibaca dengan referensi.
sumber
Seperti orang lain sudah menjawab: penggunaan Selalu referensi, kecuali makhluk variabel
NULL
/nullptr
adalah benar-benar negara yang sah.Pandangan John Carmack tentang masalah ini serupa:
http://www.altdevblogaday.com/2011/12/24/static-code-analysis/
Edit 2012-03-13
Pengguna Bret Kuhns dengan benar menyatakan :
Cukup benar, tetapi pertanyaannya tetap, bahkan ketika mengganti pointer mentah dengan pointer pintar.
Misalnya, keduanya
std::unique_ptr
danstd::shared_ptr
dapat dikonstruksikan sebagai pointer "kosong" melalui konstruktor default mereka:... yang berarti menggunakan mereka tanpa memverifikasi mereka tidak kosong berisiko macet, yang merupakan inti dari diskusi J. Carmack.
Dan kemudian, kita memiliki masalah lucu "bagaimana kita melewati pointer pintar sebagai parameter fungsi?"
Jon 's jawaban untuk pertanyaan C ++ - lewat referensi untuk meningkatkan :: shared_ptr , dan komentar berikut menunjukkan bahwa bahkan kemudian, melewati pointer cerdas dengan copy atau dengan referensi ini tidak jelas dipotong sebagai salah satu ingin (saya mendukung diriku sendiri " oleh-referensi "secara default, tapi saya bisa salah).
sumber
shared_ptr
,, danunique_ptr
. Semantik kepemilikan dan konvensi parameter in / out diurus dengan kombinasi dari ketiga bagian ini dan konst'ness. Hampir tidak perlu untuk pointer mentah di C ++ kecuali ketika berurusan dengan kode lama dan algoritma yang sangat optimal. Area-area di mana mereka digunakan harus diringkas sebisa mungkin dan mengkonversi setiap pointer mentah ke setara "modern" yang semantik.Ini bukan masalah selera. Berikut adalah beberapa aturan yang pasti.
Jika Anda ingin merujuk ke variabel yang dideklarasikan secara statis dalam lingkup yang dideklarasikan kemudian gunakan referensi C ++, dan itu akan sangat aman. Hal yang sama berlaku untuk penunjuk pintar yang dinyatakan secara statis. Melewati parameter dengan referensi adalah contoh penggunaan ini.
Jika Anda ingin merujuk sesuatu dari lingkup yang lebih luas dari lingkup yang dideklarasikan maka Anda harus menggunakan referensi smart pointer yang dihitung agar aman sepenuhnya.
Anda dapat merujuk ke elemen koleksi dengan referensi untuk kenyamanan sintaksis, tetapi itu tidak aman; elemen dapat dihapus kapan saja.
Untuk menyimpan referensi ke elemen koleksi dengan aman, Anda harus menggunakan referensi yang dihitung dengan smart pointer.
sumber
Setiap perbedaan kinerja akan sangat kecil sehingga tidak akan membenarkan menggunakan pendekatan yang kurang jelas.
Pertama, satu kasus yang tidak disebutkan di mana referensi umumnya unggul adalah
const
referensi. Untuk jenis yang tidak sederhana, melewati sebuahconst reference
menghindari membuat sementara dan tidak menyebabkan kebingungan yang Anda khawatirkan (karena nilainya tidak dimodifikasi). Di sini, memaksa seseorang untuk melewati pointer menyebabkan kebingungan yang Anda khawatirkan, karena melihat alamat yang diambil dan diteruskan ke fungsi mungkin membuat Anda berpikir nilainya berubah.Bagaimanapun, pada dasarnya saya setuju dengan Anda. Saya tidak suka fungsi mengambil referensi untuk mengubah nilainya ketika tidak terlalu jelas bahwa inilah fungsi yang dilakukan. Saya juga lebih suka menggunakan pointer dalam kasus itu.
Ketika Anda perlu mengembalikan nilai dalam tipe yang kompleks, saya cenderung lebih suka referensi. Sebagai contoh:
Di sini, nama fungsi menjelaskan bahwa Anda mendapatkan informasi kembali dalam sebuah array. Jadi tidak ada kebingungan.
Keuntungan utama dari referensi adalah bahwa mereka selalu mengandung nilai yang valid, lebih bersih daripada pointer, dan mendukung polimorfisme tanpa memerlukan sintaks tambahan. Jika tidak ada kelebihan ini yang berlaku, tidak ada alasan untuk lebih memilih referensi daripada pointer.
sumber
Disalin dari wiki -
Saya setuju 100% dengan ini, dan inilah mengapa saya percaya bahwa Anda hanya boleh menggunakan referensi ketika Anda memiliki alasan yang sangat bagus untuk melakukannya.
sumber
Poin yang perlu diingat:
Pointer bisa
NULL
, referensi tidak bisaNULL
.Referensi lebih mudah digunakan,
const
dapat digunakan untuk referensi ketika kita tidak ingin mengubah nilai dan hanya perlu referensi dalam suatu fungsi.Pointer digunakan dengan
*
referensi sementara digunakan dengan a&
.Gunakan pointer ketika operasi aritmatika pointer diperlukan.
Anda dapat memiliki pointer ke tipe void
int a=5; void *p = &a;
tetapi tidak dapat memiliki referensi ke tipe void.Referensi Pointer Vs
Putuskan kapan harus menggunakan apa
Pointer : Untuk array, daftar tautan, implementasi pohon, dan aritmatika pointer.
Referensi : Dalam parameter fungsi dan tipe pengembalian.
sumber
Ada masalah dengan aturan " gunakan referensi jika memungkinkan " dan muncul jika Anda ingin menyimpan referensi untuk penggunaan lebih lanjut. Untuk mengilustrasikan ini dengan contoh, bayangkan Anda memiliki kelas berikut.
Pada awalnya sepertinya ide yang baik untuk memiliki parameter dalam
RefPhone(const SimCard & card)
konstruktor yang dilewatkan oleh referensi, karena mencegah melewati salah / null pointer ke konstruktor. Entah bagaimana itu mendorong alokasi variabel pada stack dan mengambil manfaat dari RAII.Tapi kemudian orang-orang sementara datang untuk menghancurkan duniamu yang bahagia.
Jadi, jika Anda membabi buta menempel referensi Anda menukar kemungkinan melewati pointer tidak valid untuk kemungkinan menyimpan referensi ke objek yang hancur, yang pada dasarnya memiliki efek yang sama.
sunting: Perhatikan bahwa saya tetap berpegang pada aturan "Gunakan referensi di mana pun Anda bisa, petunjuk di mana pun Anda harus. Hindari petunjuk sampai Anda tidak bisa." dari jawaban yang paling banyak dipilih dan diterima (jawaban lain juga menyarankan demikian). Meskipun harus jelas, contohnya bukan untuk menunjukkan bahwa referensi seperti itu buruk. Namun mereka dapat disalahgunakan, seperti halnya pointer dan mereka dapat membawa ancaman mereka sendiri ke kode.
Ada perbedaan berikut antara petunjuk dan referensi.
Dengan mempertimbangkan itu, aturan saya saat ini adalah sebagai berikut.
sumber
const SimCard & m_card;
hanya kode yang ditulis dengan buruk.const SimCard & m_card
benar atau tidak tergantung pada konteks. Pesan dalam postingan ini bukanlah bahwa rujukan itu tidak aman (karena bisa jadi jika seseorang berusaha keras). Pesannya adalah bahwa Anda tidak harus membabi buta untuk "menggunakan referensi bila memungkinkan" mantra. Contohnya adalah hasil dari penggunaan agresif doktrin "gunakan referensi bila memungkinkan". Ini harus jelas.const SimCard & m_card
. Jika Anda ingin menjadi efisien dengan sementara, tambahkanexplicit RefPhone(const SimCard&& card)
konstruktor.Berikut ini adalah beberapa pedoman.
Suatu fungsi menggunakan data yang diteruskan tanpa memodifikasinya:
Jika objek data kecil, seperti tipe data bawaan atau struktur kecil, berikan nilai.
Jika objek data adalah array, gunakan pointer karena itu satu-satunya pilihan Anda. Buat pointer menjadi pointer ke const.
Jika objek data adalah struktur berukuran baik, gunakan pointer const atau referensi const untuk meningkatkan efisiensi program. Anda menghemat waktu dan ruang yang diperlukan untuk menyalin struktur atau desain kelas. Buat penunjuk atau referensi const.
Jika objek data adalah objek kelas, gunakan referensi const. Semantik desain kelas sering memerlukan menggunakan referensi, yang merupakan alasan utama C ++ menambahkan fitur ini. Jadi, cara standar untuk meneruskan argumen objek kelas adalah dengan referensi.
Suatu fungsi memodifikasi data dalam fungsi panggilan:
1.Jika objek data adalah tipe data bawaan, gunakan pointer. Jika Anda melihat kode seperti fixit (& x), di mana x adalah int, sudah cukup jelas bahwa fungsi ini bermaksud untuk memodifikasi x.
2.Jika objek data adalah array, gunakan satu-satunya pilihan Anda: pointer.
3.Jika objek data adalah struktur, gunakan referensi atau pointer.
4.Jika objek data adalah objek kelas, gunakan referensi.
Tentu saja, ini hanya panduan, dan mungkin ada alasan untuk membuat pilihan yang berbeda. Misalnya, cin menggunakan referensi untuk tipe dasar sehingga Anda dapat menggunakan cin >> n alih-alih cin >> & n.
sumber
Referensi lebih bersih dan lebih mudah digunakan, dan mereka melakukan pekerjaan yang lebih baik dalam menyembunyikan informasi. Referensi tidak dapat dialihkan. Jika Anda perlu menunjuk pertama ke satu objek dan kemudian ke yang lain, Anda harus menggunakan pointer. Referensi tidak boleh nol, jadi jika ada peluang bahwa objek yang dimaksud mungkin nol, Anda tidak boleh menggunakan referensi. Anda harus menggunakan pointer. Jika Anda ingin menangani manipulasi objek pada Anda sendiri yaitu jika Anda ingin mengalokasikan ruang memori untuk objek di Heap bukan di Stack Anda harus menggunakan Pointer
sumber
Anda harus menulis contoh yang benar
Itu sebabnya referensi lebih disukai jika memungkinkan ...
sumber
Hanya memasukkan uang saya. Saya hanya melakukan tes. Yang licik pada saat itu. Saya hanya membiarkan g ++ membuat file assembly dari program mini yang sama menggunakan pointer dibandingkan dengan menggunakan referensi. Saat melihat output, keduanya persis sama. Selain pemberian simbol. Jadi melihat kinerja (dalam contoh sederhana) tidak ada masalah.
Sekarang pada topik petunjuk vs referensi. IMHO saya pikir kejelasan berdiri di atas segalanya. Segera setelah saya membaca perilaku implisit, jari kaki saya mulai melengkung. Saya setuju bahwa itu adalah perilaku implisit yang bagus bahwa referensi tidak boleh NULL.
Mendereferensi pointer NULL bukan masalah. itu akan merusak aplikasi Anda dan akan mudah di-debug. Masalah yang lebih besar adalah pointer tidak diinisialisasi yang mengandung nilai tidak valid. Ini kemungkinan besar akan mengakibatkan kerusakan memori yang menyebabkan perilaku tidak terdefinisi tanpa asal yang jelas.
Di sinilah saya pikir referensi jauh lebih aman daripada petunjuk. Dan saya setuju dengan pernyataan sebelumnya, bahwa antarmuka (yang harus didokumentasikan dengan jelas, lihat desain berdasarkan kontrak, Bertrand Meyer) mendefinisikan hasil dari parameter ke suatu fungsi. Sekarang dengan mempertimbangkan semua ini, preferensi saya akan menggunakan referensi di mana pun / kapan pun memungkinkan.
sumber
Untuk pointer, Anda perlu mereka menunjuk ke sesuatu, jadi pointer membutuhkan ruang memori.
Misalnya fungsi yang mengambil pointer integer tidak akan mengambil variabel integer. Jadi, Anda perlu membuat pointer untuk yang pertama kali meneruskan ke fungsi.
Adapun referensi, itu tidak akan menghabiskan memori. Anda memiliki variabel integer, dan Anda bisa meneruskannya sebagai variabel referensi. Itu dia. Anda tidak perlu membuat variabel referensi khusus untuk itu.
sumber
&address
. Referensi tentu akan menghabiskan memori jika itu adalah anggota suatu objek, dan ditambah, semua kompiler yang ada benar-benar menerapkan referensi sebagai alamat, sehingga Anda tidak menyimpan apa pun dalam hal melewati parameter atau dereferencing.