Kapan harus menggunakan referensi vs. petunjuk


381

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.


4
Sepertinya Anda sudah membuat keputusan tentang mana yang akan digunakan saat. Secara pribadi, saya lebih suka meneruskan objek yang sedang saya tangani, apakah saya memodifikasinya atau tidak. Jika suatu fungsi mengambil pointer, itu memberitahu saya bahwa itu bekerja pada pointer, yaitu menggunakannya sebagai iterator dalam array.
Benjamin Lindley

1
@ Schnommus: Cukup adil, saya kebanyakan menggunakan TextMate. Meski begitu, saya pikir lebih disukai artinya jika dilihat sekilas.
connec

4
Bagaimana dengan add_one(a);tidak jelas apa yang aakan dimodifikasi? Dikatakan tepat di kode: tambahkan satu .
GManNickG

32
@connec: Panduan gaya Google C ++ tidak dianggap sebagai panduan gaya C ++ yang bagus. Ini adalah panduan gaya untuk bekerja dengan basis kode C ++ lama Google (yaitu bagus untuk barang-barang mereka). Menerima jawaban berdasarkan itu tidak membantu siapa pun. Hanya dengan membaca komentar dan penjelasan Anda, Anda sampai pada pertanyaan ini dengan pendapat yang sudah ditetapkan dan hanya mencari orang lain untuk mengonfirmasi pandangan Anda. Akibatnya, Anda mendasarkan pertanyaan dan jawaban untuk apa yang Anda ingin / harapkan dengar.
Martin York

1
Ini hanya diperbaiki dengan memberi nama metode addOneTo(...). Jika bukan itu yang ingin Anda lakukan, lihat saja deklarasi tersebut.
Stefan

Jawaban:


296

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:

  1. jika Anda memberikan nullptrargumen, Anda akan berada di tanah perilaku yang tidak terdefinisi;
  2. versi atribut referensi tidak memungkinkan (tanpa trik yang mudah dikenali) masalah dengan 1.
  3. versi atribut referensi lebih mudah dipahami oleh pengguna: Anda harus memberikan objek yang valid, bukan sesuatu yang bisa nol.

Jika perilaku fungsi harus bekerja dengan atau tanpa objek yang diberikan, maka menggunakan pointer sebagai atribut menunjukkan bahwa Anda dapat lulus nullptrsebagai argumen dan tidak masalah untuk fungsi. Itu semacam kontrak antara pengguna dan implementasi.


49
Saya tidak yakin bahwa pointer membuat sesuatu lebih sulit dibaca? Ini adalah konsep yang cukup sederhana dan memperjelas kapan sesuatu akan dimodifikasi. Jika sesuatu yang saya katakan lebih sulit dibaca ketika tidak ada indikasi apa yang terjadi, mengapa add_one(a)tidak mengembalikan hasilnya, daripada mengaturnya dengan referensi?
connec

46
@connec: Jika 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.
Nicol Bolas

20
Satu titik, referensi bisa merujuk ke memori yang bisa hilang semudah pointer bisa. Jadi mereka belum tentu lebih aman daripada pointer. Referensi yang bertahan dan melewati bisa sama berbahayanya dengan petunjuk.
Doug T.

6
@Klaim saya maksud pointer mentah. Saya maksudkan bahwa C ++ memiliki pointer, NULLdan nullptr, 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 gunakan boost::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 dan return nullptr;menunjukkan nilai yang hilang ... Mengapa mengimpor seluruh Boost?

5
@Klaim "menggunakan NULL adalah praktik buruk" - sekarang itu hanya konyol. Dan ifapakah sudah usang dan orang harus menggunakannya while() { 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?

62

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.


10
Hanya untuk menambahkan sedikit ke ini, panduan gaya Google mengatakan bahwa parameter input untuk fungsi harus menjadi referensi konstan dan output harus menjadi pointer. Saya suka ini karena membuatnya sangat jelas ketika Anda membaca tanda tangan fungsi apa input dan apa output.
Dan

44
@Dan: Panduan gaya Google adalah untuk kode lama Google, dan tidak boleh digunakan untuk pengkodean modern. Sebenarnya, ini adalah gaya pengkodean yang agak buruk untuk proyek baru.
GManNickG

13
@connec: Biarkan saya katakan: null adalah nilai pointer yang benar-benar valid . Di mana saja ada pointer, saya bisa memberikan nilai nol. Ergo versi kedua Anda add_oneadalah 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 .
GManNickG

8
Jika itu adalah referensi, Anda akan melihatnya sebagai kasusnya. Retort seperti itu melewatkan poin: Referensi berlaku pada tingkat bahasa yang mengacu pada objek yang ada, dan tidak mungkin nol, sedangkan pointer tidak memiliki batasan seperti itu. Saya pikir sudah jelas bahwa penegakan tingkat bahasa lebih kuat dan lebih sedikit kesalahan daripada penegakan tingkat dokumentasi. Beberapa akan mencoba untuk membalas dengan mengatakan: "Lihat, referensi nol:. 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.
GManNickG

12
Halo, saya melihat kurangnya pengacara bahasa di komentar jadi biarkan saya memperbaiki: referensi biasanya dilaksanakan oleh pointer tetapi standar mengatakan tidak ada hal seperti itu. Implementasi menggunakan beberapa mekanisme lain akan menjadi 100% keluhan.
Thomas Bonini

34

Dari C ++ FAQ Lite -

Gunakan referensi ketika Anda bisa, dan petunjuk ketika Anda harus.

Referensi biasanya lebih disukai daripada pointer setiap kali Anda tidak perlu "mengulang". Ini biasanya berarti bahwa referensi paling berguna dalam antarmuka publik kelas. Referensi biasanya muncul di kulit suatu objek, dan petunjuk di dalam.

Pengecualian untuk di atas adalah di mana parameter fungsi atau nilai kembali membutuhkan referensi "sentinel" - referensi yang tidak merujuk ke objek. Ini biasanya paling baik dilakukan dengan mengembalikan / mengambil pointer, dan memberikan pointer NULL arti khusus ini (referensi harus selalu alias objek, bukan pointer NULL dereferenced).

Catatan: Pemrogram baris C lama kadang tidak suka referensi karena mereka menyediakan semantik referensi yang tidak eksplisit dalam kode pemanggil. Namun, setelah beberapa pengalaman C ++, orang dengan cepat menyadari ini adalah bentuk penyembunyian informasi, yang merupakan aset daripada kewajiban. Misalnya, programmer harus menulis kode dalam bahasa masalah daripada bahasa mesin.


1
Saya kira Anda bisa berpendapat bahwa jika Anda menggunakan API, Anda harus terbiasa dengan apa yang dilakukannya dan tahu apakah parameter yang lulus diubah atau tidak ... sesuatu untuk dipertimbangkan, tetapi saya menemukan diri saya setuju dengan programmer C ( meskipun saya sudah sedikit pengalaman C sendiri). Saya akan menambahkan bahwa sintaks yang lebih jelas bermanfaat bagi programmer dan mesin.
connec

1
@connec: Tentu saja programmer C sudah benar untuk bahasa mereka. Tetapi jangan membuat kesalahan dengan memperlakukan C ++ sebagai C. Ini adalah bahasa yang sama sekali berbeda. Jika Anda memperlakukan C ++ sebagai C, Anda akhirnya menulis apa yang dirujuk sebagai C with class(yang bukan C ++).
Martin York

15

Aturan praktis saya adalah:

  • Gunakan pointer untuk parameter keluar atau masuk / keluar. Jadi bisa dilihat bahwa nilainya akan berubah. (Anda harus menggunakan &)
  • Gunakan pointer jika parameter NULL adalah nilai yang dapat diterima. (Pastikan constjika ini merupakan parameter yang masuk)
  • Gunakan referensi untuk parameter yang masuk jika tidak bisa NULL dan bukan tipe primitif ( const T&).
  • Gunakan pointer atau pointer pintar saat mengembalikan objek yang baru dibuat.
  • Gunakan pointer atau pointer cerdas sebagai anggota struct atau kelas alih-alih referensi.
  • Gunakan referensi untuk aliasing (mis. int &current = someArray[i])

Apa pun yang Anda gunakan, jangan lupa untuk mendokumentasikan fungsi Anda dan arti parameternya jika tidak jelas.


14

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,

add_one(&a);

mungkin lebih jelas dari

add_one(a);

karena Anda tahu bahwa kemungkinan besar nilai a akan berubah. Namun di sisi lain, tanda tangan dari fungsi

void add_one(int* const n);

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

foo(int* const a, int b);

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.


Terima kasih atas demonstrasi yang jelas di mana kedua solusi mendapatkan dan kehilangan kejelasan. Saya awalnya di kamp penunjuk, tapi ini masuk akal.
Zach Beavon-Collin

12

Seperti orang lain sudah menjawab: penggunaan Selalu referensi, kecuali makhluk variabel NULL/ nullptradalah benar-benar negara yang sah.

Pandangan John Carmack tentang masalah ini serupa:

Pointer NULL adalah masalah terbesar dalam C / C ++, setidaknya dalam kode kami. Penggunaan ganda nilai tunggal sebagai bendera dan alamat menyebabkan sejumlah masalah fatal yang luar biasa. Referensi C ++ harus lebih disukai daripada pointer bila memungkinkan; sementara referensi "benar-benar" hanyalah sebuah pointer, ia memiliki kontrak implisit untuk tidak-NULL. Lakukan pemeriksaan NULL ketika pointer diubah menjadi referensi, maka Anda dapat mengabaikan masalah setelahnya.

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

Edit 2012-03-13

Pengguna Bret Kuhns dengan benar menyatakan :

Standar C ++ 11 telah diselesaikan. Saya pikir sudah waktunya di utas ini untuk menyebutkan bahwa sebagian besar kode harus baik-baik saja dengan kombinasi referensi, shared_ptr, dan unique_ptr.

Cukup benar, tetapi pertanyaannya tetap, bahkan ketika mengganti pointer mentah dengan pointer pintar.

Misalnya, keduanya std::unique_ptrdan std::shared_ptrdapat 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).


1
Standar C ++ 11 telah diselesaikan. Saya pikir sudah waktunya di utas ini untuk menyebutkan bahwa sebagian besar kode harus baik - baik saja dengan kombinasi referensi shared_ptr,, dan unique_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.
Bret Kuhns

1
Banyak smart pointer seharusnya tidak dibagikan, tetapi harus diuji untuk null-ness dan kemudian objek yang terkandung dilewatkan dengan referensi. Satu-satunya waktu Anda benar-benar harus melewati pointer cerdas adalah ketika Anda mentransfer kepemilikan (unique_ptr) atau berbagi kepemilikan (shared_ptr) dengan objek lain.
Luke Senilai

@povman: Saya sepenuhnya setuju: Jika kepemilikan bukan bagian dari antarmuka (dan kecuali jika akan dimodifikasi, itu tidak boleh), maka kita tidak boleh melewatkan smart pointer sebagai parameter (atau nilai pengembalian). Masalahnya menjadi sedikit lebih rumit ketika kepemilikan adalah bagian dari antarmuka. Misalnya, debat Sutter / Meyers tentang cara mengoper Unique_ptr sebagai parameter: dengan menyalin (Sutter) atau dengan r-value reference (Meyers)? Sebuah antipattern bergantung pada pengedaran pointer ke global shared_ptr, dengan risiko pointer tersebut tidak valid (solusinya adalah menyalin smart pointer di stack)
paercebal

7

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.


5

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 constreferensi. Untuk jenis yang tidak sederhana, melewati sebuah const referencemenghindari 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:

bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative

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.


4

Disalin dari wiki -

Konsekuensi dari ini adalah bahwa dalam banyak implementasi, beroperasi pada variabel dengan masa hidup otomatis atau statis melalui referensi, meskipun secara sintaksis mirip dengan mengaksesnya secara langsung, dapat melibatkan operasi dereferensi tersembunyi yang mahal. Referensi adalah fitur kontroversial sintaksis dari C ++ karena mereka mengaburkan tingkat tipuan pengidentifikasi; yaitu, tidak seperti kode C di mana pointer biasanya menonjol secara sintaksis, dalam blok besar kode C ++ mungkin tidak langsung jelas jika objek yang diakses didefinisikan sebagai variabel lokal atau global atau apakah itu referensi (pointer implisit) ke beberapa lokasi lain, terutama jika kode menggabungkan referensi dan pointer. Aspek ini dapat membuat kode C ++ yang ditulis dengan buruk lebih sulit untuk dibaca dan di-debug (lihat Mengasingkan).

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.


Saya juga setuju untuk sebagian besar, tetapi saya datang ke pandangan bahwa hilangnya perlindungan built-in terhadap pointer NULL agak terlalu mahal untuk masalah sintaksis murni, terutama karena - meskipun lebih eksplisit - sintaks pointer cukup jelek bagaimanapun.
connec

Saya kira keadaan akan menjadi faktor penting juga. Saya pikir mencoba menggunakan referensi ketika basis kode saat ini sebagian besar menggunakan pointer akan menjadi ide yang buruk. Jika Anda berharap mereka menjadi referensi maka fakta bahwa mereka begitu implisit mungkin kurang penting ..
user606723

3

Poin yang perlu diingat:

  1. Pointer bisa NULL, referensi tidak bisa NULL.

  2. Referensi lebih mudah digunakan, constdapat digunakan untuk referensi ketika kita tidak ingin mengubah nilai dan hanya perlu referensi dalam suatu fungsi.

  3. Pointer digunakan dengan *referensi sementara digunakan dengan a &.

  4. Gunakan pointer ketika operasi aritmatika pointer diperlukan.

  5. Anda dapat memiliki pointer ke tipe void int a=5; void *p = &a;tetapi tidak dapat memiliki referensi ke tipe void.

Referensi Pointer Vs

void fun(int *a)
{
    cout<<a<<'\n'; // address of a = 0x7fff79f83eac
    cout<<*a<<'\n'; // value at a = 5
    cout<<a+1<<'\n'; // address of a increment by 4 bytes(int) = 0x7fff79f83eb0
    cout<<*(a+1)<<'\n'; // value here is by default = 0
}
void fun(int &a)
{
    cout<<a<<'\n'; // reference of original a passed a = 5
}
int a=5;
fun(&a);
fun(a);

Putuskan kapan harus menggunakan apa

Pointer : Untuk array, daftar tautan, implementasi pohon, dan aritmatika pointer.

Referensi : Dalam parameter fungsi dan tipe pengembalian.


2

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.

class SimCard
{
    public:
        explicit SimCard(int id):
            m_id(id)
        {
        }

        int getId() const
        {
            return m_id;
        }

    private:
        int m_id;
};

class RefPhone
{
    public:
        explicit RefPhone(const SimCard & card):
            m_card(card)
        {
        }

        int getSimId()
        {
            return m_card.getId();
        }

    private:
        const SimCard & m_card;
};

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.

PtrPhone nullPhone(0);  //this will not happen that easily
SimCard * cardPtr = new SimCard(666);  //evil pointer
delete cardPtr;  //muahaha
PtrPhone uninitPhone(cardPtr);  //this will not happen that easily

Tapi kemudian orang-orang sementara datang untuk menghancurkan duniamu yang bahagia.

RefPhone tempPhone(SimCard(666));   //evil temporary
//function referring to destroyed object
tempPhone.getSimId();    //this can happen

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.

  1. Ketika datang ke variabel lewat, lulus dengan referensi terlihat seperti lewat nilai, tetapi memiliki semantik pointer (bertindak seperti pointer).
  2. Referensi tidak dapat langsung diinisialisasi ke 0 (nol).
  3. Referensi (referensi, bukan objek referensi) tidak dapat dimodifikasi (setara dengan pointer "* const").
  4. referensi const dapat menerima parameter sementara.
  5. Referensi const lokal memperpanjang umur objek sementara

Dengan mempertimbangkan itu, aturan saya saat ini adalah sebagai berikut.

  • Gunakan referensi untuk parameter yang akan digunakan secara lokal dalam lingkup fungsi.
  • Gunakan pointer ketika 0 (nol) adalah nilai parameter yang dapat diterima atau Anda perlu menyimpan parameter untuk penggunaan lebih lanjut. Jika 0 (nol) dapat diterima saya menambahkan akhiran "_n" ke parameter, gunakan pointer yang dijaga (seperti QPointer di Qt) atau hanya mendokumentasikannya. Anda juga dapat menggunakan pointer pintar. Anda harus lebih berhati-hati dengan pointer bersama daripada dengan pointer normal (jika tidak, Anda dapat berakhir dengan kebocoran memori desain dan kekacauan tanggung jawab).

3
Masalah dengan contoh Anda bukanlah bahwa referensi tidak aman, tetapi bahwa Anda mengandalkan sesuatu di luar lingkup objek objek Anda untuk menjaga anggota pribadi Anda tetap hidup. const SimCard & m_card;hanya kode yang ditulis dengan buruk.
plamenko

@plamenko Saya khawatir Anda tidak mengerti tujuan dari contoh ini. Entah const SimCard & m_cardbenar 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.
doc

Ada dua hal yang mengganggu saya dengan jawaban Anda karena saya pikir itu mungkin menyesatkan seseorang yang mencoba mempelajari lebih lanjut tentang masalah ini. 1. Posting ini searah dan mudah untuk mendapatkan kesan bahwa referensi itu buruk. Anda hanya memberikan satu contoh bagaimana tidak menggunakan referensi. 2. Anda tidak jelas dalam contoh Anda apa yang salah dengan itu. Ya, sementara akan mendapatkan destroyet, tapi itu bukan garis yang salah, itu adalah implementasi kelas.
plamenko

Praktis Anda seharusnya tidak pernah memiliki anggota seperti const SimCard & m_card. Jika Anda ingin menjadi efisien dengan sementara, tambahkan explicit RefPhone(const SimCard&& card)konstruktor.
plamenko

@plamenko jika Anda tidak dapat membaca dengan pemahaman dasar maka Anda memiliki masalah yang lebih besar daripada hanya disesatkan oleh posting saya. Saya tidak tahu bagaimana saya bisa lebih jelas. Lihatlah kalimat pertama. Ada masalah dengan mantra "gunakan referensi jika memungkinkan"! Di mana dalam posting saya Anda telah menemukan pernyataan bahwa referensi buruk? Di akhir posting saya, Anda telah menulis di mana harus menggunakan referensi, jadi bagaimana Anda bisa sampai pada kesimpulan seperti itu? Ini bukan jawaban langsung untuk pertanyaan itu?
doc

1

Berikut ini adalah beberapa pedoman.

Suatu fungsi menggunakan data yang diteruskan tanpa memodifikasinya:

  1. Jika objek data kecil, seperti tipe data bawaan atau struktur kecil, berikan nilai.

  2. Jika objek data adalah array, gunakan pointer karena itu satu-satunya pilihan Anda. Buat pointer menjadi pointer ke const.

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

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


0

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

int *pInt = new int; // allocates *pInt on the Heap

0

Anda harus menulis contoh yang benar

void add_one(int& n) { n += 1; }
void add_one(int* const n)
{
  if (n)
    *n += 1;
}

Itu sebabnya referensi lebih disukai jika memungkinkan ...


-1

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.


-2

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.


4
Nggak. Fungsi yang mengambil pointer tidak memerlukan alokasi variabel pointer: Anda bisa melewati sementara &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.
underscore_d
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.