Pointer vs. Referensi

256

Apa yang akan menjadi praktik yang lebih baik ketika memberikan fungsi variabel asli untuk bekerja dengan:

unsigned long x = 4;

void func1(unsigned long& val) {
     val = 5;            
}
func1(x);

atau:

void func2(unsigned long* val) {
     *val = 5;
}
func2(&x);

TKI: Apakah ada alasan untuk memilih satu dari yang lain?

Jack Reza
sumber
1
Referensi tentu saja berharga, tetapi saya berasal dari C, di mana pointer ada di mana-mana. Kita harus mahir dengan pointer terlebih dahulu untuk memahami nilai referensi.
Jay D
Bagaimana ini cocok dengan tujuan seperti transparansi referensial dari pemrograman fungsional? Bagaimana jika Anda selalu ingin fungsi mengembalikan objek baru dan tidak pernah secara internal mengubah status, terutama variabel yang tidak diteruskan ke fungsi. Apakah ada cara konsep ini masih digunakan dengan pointer dan referensi dalam bahasa seperti C ++. (Catatan, saya berasumsi seseorang sudah memiliki tujuan transparansi referensial. Saya tidak tertarik membicarakan apakah itu tujuan yang baik atau tidak.)
ely
Lebih suka referensi. Petunjuk pengguna saat Anda tidak punya pilihan.
Ferruccio

Jawaban:

285

Aturan praktis saya adalah:

Gunakan pointer jika Anda ingin melakukan aritmatika pointer dengan mereka (misalnya, menambah alamat pointer untuk melangkah melalui array) atau jika Anda harus melewati pointer NULL.

Gunakan referensi sebaliknya.

Nils Pipenbrinck
sumber
10
Excelent point tentang pointer menjadi NULL. Jika Anda memiliki parameter pointer maka Anda harus memeriksa secara eksplisit bahwa itu bukan NULL, atau mencari semua penggunaan fungsi untuk memastikan bahwa itu tidak pernah NULL. Upaya ini tidak diperlukan untuk referensi.
Richard Corden
26
Jelaskan apa yang Anda maksud dengan aritmatika. Seorang pengguna baru mungkin tidak mengerti bahwa Anda ingin menyesuaikan apa yang ditunjuk oleh pointer.
Martin York
7
Martin, Dengan aritmatika yang saya maksudkan adalah Anda memberikan pointer ke sebuah struktur tetapi tahu bahwa itu bukan struktur yang sederhana tetapi sebuah array darinya. Dalam hal ini Anda bisa mengindeksnya menggunakan [] atau melakukan aritmatika dengan menggunakan ++ / - pada pointer. Singkatnya perbedaannya.
Nils Pipenbrinck
2
Martin, Anda hanya bisa melakukan ini dengan pointer secara langsung. Tidak dengan referensi. Tentu Anda dapat mengambil pointer ke referensi dan melakukan hal yang sama dalam praktiknya, tetapi jika Anda melakukannya Anda berakhir dengan kode yang sangat kotor ..
Nils Pipenbrinck
1
Bagaimana dengan polimorfisme (mis. Base* b = new Derived())? Ini seperti kasus yang tidak bisa ditangani tanpa petunjuk.
Chris Redford
72

Saya benar-benar berpikir Anda akan mendapat manfaat dari membangun pedoman pemanggilan fungsi panggilan berikut:

  1. Seperti di semua tempat lain, selalu benar- constbenar.

    • Catatan: Ini berarti, antara lain, bahwa hanya nilai di luar (lihat item 3) dan nilai yang diteruskan oleh nilai (lihat item 4) dapat kekurangan constspecifier.
  2. Hanya berikan nilai dengan pointer jika nilai 0 / NULL adalah input yang valid dalam konteks saat ini.

    • Dasar Pemikiran 1: Sebagai penelepon , Anda melihat bahwa apa pun yang Anda lewati harus dalam kondisi yang dapat digunakan.

    • Dasar Pemikiran 2: Seperti yang disebut , Anda tahu bahwa apa pun yang masuk adalah dalam kondisi yang dapat digunakan. Oleh karena itu, tidak ada pemeriksaan NULL atau penanganan kesalahan yang perlu dilakukan untuk nilai tersebut.

    • Dasar Pemikiran 3: Dasar pemikiran 1 dan 2 akan diberlakukan kompiler . Selalu tangkap kesalahan pada waktu kompilasi jika Anda bisa.

  3. Jika argumen fungsi adalah nilai keluar, maka sampaikan dengan referensi.

    • Dasar Pemikiran: Kami tidak ingin merusak item 2 ...
  4. Pilih "pass by value" over "pass by const reference" hanya jika nilainya adalah POD ( Plain old Datastructure ) atau cukup kecil (berdasarkan memori) atau dengan cara lain cukup murah (berdasarkan waktu) untuk disalin.

    • Dasar Pemikiran: Hindari salinan yang tidak perlu.
    • Catatan: cukup kecil dan cukup murah tidak dapat diukur secara absolut.
Johann Gerell
sumber
Ini tidak memiliki pedoman ketika: ... "kapan harus menggunakan const &" ... Pedoman 2 harus ditulis "untuk nilai [dalam], hanya melewati pointer jika NULL valid. Jika tidak, gunakan referensi const (atau untuk" objek "kecil, salin), atau referensi jika ini merupakan nilai [keluar]. Saya memantau pos ini untuk berpotensi menambahkan +1.
paercebal
Item 1 mencakup case yang Anda gambarkan.
Johann Gerell
Agak sulit untuk melewatkan parameter-keluar dengan referensi jika tidak dibangun-standar. Itu cukup umum dalam kode saya - seluruh alasan untuk memiliki fungsi membuat objek itu adalah karena itu non-sepele.
MSalters
@ MSalters: Jika Anda akan mengalokasikan memori di dalam fungsi (yang saya pikir adalah apa yang Anda maksud), lalu mengapa tidak mengembalikan pointer ke memori yang dialokasikan?
Kleist
@Kleist: Atas nama @MSalters, ada banyak alasan yang memungkinkan. Salah satunya adalah bahwa Anda mungkin sudah mengalokasikan memori untuk diisi, seperti ukuran pra std::vector<>.
Johann Gerell
24

Ini akhirnya menjadi subyektif. Diskusi sejauh ini bermanfaat, tetapi saya tidak berpikir ada jawaban yang benar atau menentukan untuk ini. Banyak yang akan tergantung pada pedoman gaya dan kebutuhan Anda saat itu.

Meskipun ada beberapa kemampuan yang berbeda (apakah sesuatu bisa NULL atau tidak) dengan sebuah pointer, perbedaan praktis terbesar untuk parameter output adalah murni sintaksis. Panduan Gaya C ++ Google ( https://google.github.io/styleguide/cppguide.html#Reference_Arguments ), misalnya, mandat hanya pointer untuk parameter output, dan hanya memperbolehkan referensi yang konst. Alasannya adalah salah satu keterbacaan: sesuatu dengan sintaks nilai seharusnya tidak memiliki makna semantik pointer. Saya tidak menyarankan bahwa ini benar atau salah, tetapi saya pikir intinya di sini adalah masalah gaya, bukan kebenaran.

Aaron N. Tubbs
sumber
Apa artinya bahwa referensi memiliki sintaks nilai tetapi makna penunjuk semantik?
Eric Andrew Lewis
Sepertinya Anda meneruskan salinan karena bagian "pass by reference" hanya terlihat dari definisi funciton (sintaks nilai), tetapi Anda tidak menyalin nilai yang Anda lewati, pada dasarnya Anda melewati sebuah pointer di bawah kap, yang memungkinkan fungsi untuk mengubah nilai Anda.
phant0m
Orang tidak boleh lupa bahwa panduan gaya Google C ++ banyak dibenci.
Deduplicator
7

Anda harus melewati pointer jika Anda akan mengubah nilai variabel. Meskipun secara teknis melewati referensi atau pointer adalah sama, melewati pointer dalam use case Anda lebih mudah dibaca karena "mengiklankan" fakta bahwa nilainya akan diubah oleh fungsi.

Max Caceres
sumber
2
Jika Anda mengikuti panduan Johann Gerell, referensi non-const juga mengiklankan variabel yang dapat diubah, sehingga pointer tidak memiliki keunggulan di sini.
Alexander Kondratskiy
4
@AlexanderKondratskiy: Anda tidak mengerti intinya ... Anda tidak dapat melihat langsung di situs panggilan apakah fungsi yang dipanggil menerima paramter sebagai constatau bukan constreferensi, tetapi Anda dapat melihat apakah parameternya dilewatkan secara default &xvs x, dan gunakan konvensi itu untuk menyandikan apakah parameter tersebut dapat dimodifikasi. (Yang mengatakan, ada saat-saat ketika Anda ingin melewati sebuah constpointer, jadi konvensioni hanyalah sebuah petunjuk. Diduga mencurigai sesuatu mungkin dimodifikasi ketika itu tidak akan kurang berbahaya daripada berpikir itu tidak akan menjadi ketika itu akan menjadi ....)
Tony Delroy
5

Jika Anda memiliki parameter di mana Anda mungkin perlu menunjukkan tidak adanya nilai, itu praktik umum untuk membuat parameter nilai penunjuk dan meneruskan NULL.

Solusi yang lebih baik dalam banyak kasus (dari perspektif keamanan) adalah dengan menggunakan boost :: opsional . Ini memungkinkan Anda untuk memberikan nilai opsional dengan referensi dan juga sebagai nilai balik.

// Sample method using optional as input parameter
void PrintOptional(const boost::optional<std::string>& optional_str)
{
    if (optional_str)
    {
       cout << *optional_str << std::endl;
    }
    else
    {
       cout << "(no string)" << std::endl;
    }
}

// Sample method using optional as return value
boost::optional<int> ReturnOptional(bool return_nothing)
{
    if (return_nothing)
    {
       return boost::optional<int>();
    }

    return boost::optional<int>(42);
}
Kiley Hykawy
sumber
4

Pointer

  • Pointer adalah variabel yang menyimpan alamat memori.
  • Deklarasi pointer terdiri dari tipe dasar, *, dan nama variabel.
  • Pointer dapat menunjuk ke sejumlah variabel dalam seumur hidup
  • Pointer yang saat ini tidak menunjuk ke lokasi memori yang valid diberi nilai nol (Yang nol)

    BaseType* ptrBaseType;
    BaseType objBaseType;
    ptrBaseType = &objBaseType;
  • & Adalah operator unary yang mengembalikan alamat memori operannya.

  • Operator referensi (*) digunakan untuk mengakses nilai yang disimpan dalam variabel yang ditunjuk oleh pointer.

       int nVar = 7;
       int* ptrVar = &nVar;
       int nVar2 = *ptrVar;

Referensi

  • Referensi (&) seperti alias ke variabel yang ada.

  • Referensi (&) seperti pointer konstan yang secara otomatis direferensikan.

  • Biasanya digunakan untuk daftar argumen fungsi dan nilai pengembalian fungsi.

  • Referensi harus diinisialisasi ketika dibuat.

  • Setelah referensi diinisialisasi ke suatu objek, itu tidak dapat diubah untuk merujuk ke objek lain.

  • Anda tidak dapat memiliki referensi NULL.

  • Referensi const dapat merujuk ke const int. Ini dilakukan dengan variabel sementara dengan nilai const

    int i = 3;    //integer declaration
    int * pi = &i;    //pi points to the integer i
    int& ri = i;    //ri is refers to integer i – creation of reference and initialization

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Saurabh Raoot
sumber
3

Referensi adalah pointer implisit. Pada dasarnya Anda dapat mengubah nilai referensi ke titik tetapi Anda tidak dapat mengubah referensi untuk menunjuk ke sesuatu yang lain. Jadi 2 sen saya adalah bahwa jika Anda hanya ingin mengubah nilai suatu parameter, letakkan sebagai referensi tetapi jika Anda perlu mengubah parameter untuk menunjuk ke objek yang berbeda, lewati dengan menggunakan pointer.


sumber
3

Pertimbangkan kata kunci C # yang keluar. Kompiler memerlukan penelepon metode untuk menerapkan kata kunci keluar untuk argumen apa pun, meskipun ia sudah tahu jika mereka. Ini dimaksudkan untuk meningkatkan keterbacaan. Meskipun dengan IDE modern saya cenderung berpikir bahwa ini adalah pekerjaan untuk menyoroti sintaks (atau semantik).

Daniel Earwicker
sumber
typo: semantic, not symantic; +1 Saya setuju tentang kemungkinan menyorot alih-alih menulis (C #), atau & (dalam kasus C, tidak ada referensi)
peenut
2

Lewati referensi const kecuali ada alasan Anda ingin mengubah / menyimpan konten yang Anda lewati.

Ini akan menjadi metode yang paling efisien dalam banyak kasus.

Pastikan Anda menggunakan const pada setiap parameter yang tidak ingin Anda ubah, karena ini tidak hanya melindungi Anda dari melakukan sesuatu yang bodoh dalam fungsi, itu memberikan indikasi yang baik kepada pengguna lain apa fungsi yang dilakukannya terhadap nilai yang diteruskan. Ini termasuk membuat pointer pointer ketika Anda hanya ingin mengubah ...

Bukan Garvis
sumber
2

Pointer:

  • Dapat ditugaskan nullptr(atau NULL).
  • Di situs panggilan, Anda harus menggunakan &jika tipe Anda bukan pointer itu sendiri, membuat Anda secara eksplisit memodifikasi objek Anda.
  • Pointer bisa rebound.

Referensi:

  • Tidak boleh nol.
  • Sekali terikat, tidak bisa berubah.
  • Penelepon tidak perlu menggunakan secara eksplisit &. Ini kadang-kadang dianggap buruk karena Anda harus pergi ke implementasi fungsi untuk melihat apakah parameter Anda diubah.
Germán Diago
sumber
Poin kecil bagi mereka yang tidak tahu: nullptr atau NULL hanyalah 0. stackoverflow.com/questions/462165/…
Serguei Fedorov
2
nullptr tidak sama dengan 0. Coba int a = nullptr; stackoverflow.com/questions/1282295/what-exactly-is-nullptr
Johan Lundberg
0

Referensi mirip dengan pointer, kecuali bahwa Anda tidak perlu menggunakan awalan ∗ untuk mengakses nilai yang dirujuk oleh referensi. Juga, referensi tidak dapat dibuat untuk merujuk ke objek yang berbeda setelah inisialisasi.

Referensi sangat berguna untuk menentukan argumen fungsi.

untuk informasi lebih lanjut lihat "Tur C ++" oleh "Bjarne Stroustrup" (2014) Halaman 11-12

amirfg
sumber