Saya baru mengenal pemrograman C ++, tetapi saya memiliki pengalaman di Java. Saya perlu panduan tentang cara meneruskan objek ke fungsi di C ++.
Apakah saya perlu meneruskan nilai pointer, referensi, atau non-pointer dan non-referensi? Saya ingat di Jawa tidak ada masalah seperti itu karena kita hanya melewati variabel yang memegang referensi ke objek.
Alangkah baiknya jika Anda juga bisa menjelaskan di mana harus menggunakan masing-masing opsi tersebut.
c++
pointers
pass-by-reference
pass-by-value
c++-faq
Rakesh K
sumber
sumber
Jawaban:
Aturan praktis untuk C ++ 11:
Lewati nilai , kecuali saat
const
referensi ,const
referensi non- nilai ,const
referensi atau tidak.)Melewati oleh pointer hampir tidak pernah disarankan. Parameter opsional paling baik dinyatakan sebagai
std::optional
(boost::optional
untuk ld std yang lebih tua), dan aliasing dilakukan dengan baik dengan referensi.Semantik bergerak C ++ 11 membuat passing dan pengembalian dengan nilai jauh lebih menarik bahkan untuk objek yang kompleks.
Aturan praktis untuk C ++ 03:
Lewati argumen dengan
const
referensi , kecuali saatconst
referensiNULL
/0
/nullptr
sebagai gantinya; menerapkan aturan sebelumnya untuk menentukan apakah Anda harus melewati pointer keconst
argumen(di sini, "pass by value" disebut "pass by copy", karena lewat nilai selalu membuat salinan dalam C ++ 03)
Ada lebih dari ini, tetapi beberapa aturan pemula ini akan membuat Anda cukup jauh.
sumber
Ada beberapa perbedaan dalam konvensi pemanggilan di C ++ dan Java. Dalam C ++, secara teknis hanya ada dua konvensi: pass-by-value dan pass-by-reference, dengan beberapa literatur termasuk konvensi pass-by-pointer ketiga (yang sebenarnya pass-by-value dari tipe pointer). Di atas semua itu, Anda bisa menambahkan konstanta pada tipe argumen, meningkatkan semantik.
Lewati dengan referensi
Melewati dengan referensi berarti bahwa fungsi tersebut secara konseptual akan menerima instance objek Anda dan bukan salinannya. Referensi secara konseptual merupakan alias untuk objek yang digunakan dalam konteks panggilan, dan tidak bisa nol. Semua operasi yang dilakukan di dalam fungsi berlaku untuk objek di luar fungsi. Konvensi ini tidak tersedia di Jawa atau C.
Pass by value (dan pass-by-pointer)
Kompiler akan menghasilkan salinan objek dalam konteks panggilan dan menggunakan salinan itu di dalam fungsi. Semua operasi yang dilakukan di dalam fungsi dilakukan untuk menyalin, bukan elemen eksternal. Ini adalah konvensi untuk tipe primitif di Jawa.
Versi khusus itu lewat pointer (alamat-objek) ke fungsi. Fungsi menerima pointer, dan setiap dan semua operasi yang diterapkan pada pointer itu sendiri diterapkan ke copy (pointer), di sisi lain, operasi yang diterapkan pada pointer dereferenced akan berlaku untuk instance objek di lokasi memori itu, sehingga fungsi dapat memiliki efek samping. Efek menggunakan nilai pass-by-pointer ke objek akan memungkinkan fungsi internal untuk memodifikasi nilai-nilai eksternal, seperti dengan pass-by-reference dan juga akan memungkinkan untuk nilai-nilai opsional (melewati pointer nol).
Ini adalah konvensi yang digunakan dalam C ketika suatu fungsi perlu memodifikasi variabel eksternal, dan konvensi yang digunakan di Jawa dengan tipe referensi: referensi disalin, tetapi objek yang dirujuk adalah sama: perubahan pada referensi / penunjuk tidak terlihat di luar fungsi, tetapi perubahan memori menunjuk.
Menambahkan const ke persamaan
Dalam C ++ Anda dapat menetapkan konstan-objek untuk objek ketika mendefinisikan variabel, pointer dan referensi pada level yang berbeda. Anda dapat mendeklarasikan variabel sebagai konstan, Anda dapat mendeklarasikan referensi ke instance konstan, dan Anda dapat mendefinisikan semua pointer ke objek konstan, pointer konstan ke objek yang bisa berubah dan pointer konstan ke elemen konstan. Sebaliknya di Jawa, Anda hanya dapat menentukan satu tingkat kekenyalan (kata kunci akhir): variabel (contoh untuk tipe primitif, referensi untuk tipe referensi), tetapi Anda tidak dapat menentukan referensi ke elemen yang tidak dapat diubah (kecuali kelas itu sendiri adalah kekal).
Ini banyak digunakan dalam konvensi pemanggilan C ++. Ketika objek kecil, Anda bisa melewati objek dengan nilai. Kompiler akan menghasilkan salinan, tetapi salinan itu bukan operasi yang mahal. Untuk jenis lain, jika fungsi tidak akan mengubah objek, Anda dapat meneruskan referensi ke instance konstan (biasanya disebut referensi konstan) dari tipe tersebut. Ini tidak akan menyalin objek, tetapi meneruskannya ke fungsi. Tetapi pada saat yang sama kompiler akan menjamin bahwa objek tidak berubah di dalam fungsi.
Aturan praktis
Ini adalah beberapa aturan dasar yang harus diikuti:
Ada penyimpangan kecil lainnya dari aturan ini, yang pertama adalah menangani kepemilikan suatu objek. Ketika suatu objek dialokasikan secara dinamis dengan yang baru, itu harus dialokasikan dengan menghapus (atau versi [] daripadanya). Objek atau fungsi yang bertanggung jawab atas penghancuran objek dianggap sebagai pemilik sumber daya. Ketika objek yang dialokasikan secara dinamis dibuat dalam sepotong kode, tetapi kepemilikan ditransfer ke elemen yang berbeda itu biasanya dilakukan dengan semantik pass-by-pointer, atau jika mungkin dengan smart pointer.
Catatan samping
Penting untuk menekankan pentingnya perbedaan antara C ++ dan referensi Java. Dalam C ++ referensi secara konseptual adalah instance dari objek, bukan accessor untuk itu. Contoh paling sederhana adalah menerapkan fungsi swap:
Fungsi swap di atas mengubah kedua argumennya melalui penggunaan referensi. Kode terdekat di Jawa:
Versi kode Java akan memodifikasi salinan referensi secara internal, tetapi tidak akan memodifikasi objek aktual secara eksternal. Referensi Java adalah pointer C tanpa aritmatika pointer yang diteruskan oleh nilai ke dalam fungsi.
sumber
Ada beberapa kasus yang perlu dipertimbangkan.
Parameter yang dimodifikasi ("keluar" dan "masuk / keluar" parameter)
Kasus ini sebagian besar tentang gaya: apakah Anda ingin kode tersebut terlihat seperti panggilan (obj) atau panggilan (& obj) ? Namun, ada dua titik di mana perbedaannya penting: kasing opsional, di bawah, dan Anda ingin menggunakan referensi saat membebani operator.
... dan opsional
Parameter tidak diubah
Ini adalah kasus yang menarik. Aturan praktisnya adalah "murah untuk menyalin" jenis disahkan oleh nilai - ini umumnya jenis kecil (tetapi tidak selalu) - sementara yang lain disahkan oleh const ref. Namun, jika Anda perlu membuat salinan di dalam fungsi Anda, Anda harus memberikan nilai . (Ya, ini memperlihatkan sedikit detail implementasi. C'est le C ++. )
... dan opsional
Ada sedikit perbedaan di sini antara semua situasi, jadi pilihlah yang membuat hidup Anda lebih mudah.
Nilai konstan adalah detail implementasi
Deklarasi ini sebenarnya adalah fungsi yang sama persis! Ketika melewati nilai, const sepenuhnya merupakan detail implementasi. Cobalah:
sumber
const
menjadi implementasi saat melewati nilai.Lewati nilai:
Lewati variabel dengan nilai ketika fungsi tersebut membutuhkan isolasi lengkap dari lingkungan yaitu untuk mencegah fungsi dari memodifikasi variabel asli serta untuk mencegah utas lain dari memodifikasi nilainya saat fungsi sedang dieksekusi.
Kelemahannya adalah siklus CPU dan memori tambahan yang dihabiskan untuk menyalin objek.
Lewati referensi const:
Formulir ini mengemulasi perilaku pass-by-value sambil menghapus overhead salin. Fungsi mendapat akses baca ke objek asli, tetapi tidak dapat mengubah nilainya.
Kelemahannya adalah keamanan utas: setiap perubahan yang dilakukan pada objek asli oleh utas lain akan muncul di dalam fungsi saat masih dijalankan.
Lewati referensi non-const:
Gunakan ini ketika fungsi harus menulis kembali beberapa nilai ke variabel, yang pada akhirnya akan digunakan oleh pemanggil.
Sama seperti kasus referensi const, ini bukan thread-safe.
Lewati oleh pointer pointer:
Secara fungsional sama dengan pass by const-reference kecuali untuk sintaks yang berbeda, ditambah fakta bahwa fungsi pemanggilan dapat melewati NULL pointer untuk menunjukkan ia tidak memiliki data yang valid untuk dilewati.
Tidak aman untuk benang.
Lewati dengan pointer non-const:
Mirip dengan referensi non-const. Penelepon biasanya mengatur variabel ke NULL ketika fungsi tidak seharusnya menulis kembali nilai. Konvensi ini terlihat di banyak API glibc. Contoh:
Sama seperti semua yang dilewati oleh referensi / pointer, bukan thread-safe.
sumber
Karena tidak ada yang disebutkan saya menambahkannya, Ketika Anda melewatkan objek ke fungsi di c ++, copy constructor default objek dipanggil jika Anda tidak memiliki satu yang membuat klon objek dan kemudian meneruskannya ke metode, jadi ketika Anda mengubah nilai objek yang akan mencerminkan pada salinan objek, bukan objek asli, itu adalah masalah dalam c ++, Jadi jika Anda membuat semua atribut kelas menjadi pointer, maka copy constructor akan menyalin alamat dari atribut pointer, jadi ketika metode invokasi pada objek yang memanipulasi nilai-nilai yang disimpan dalam alamat atribut pointer, perubahan juga mencerminkan dalam objek asli yang dilewatkan sebagai parameter, jadi ini bisa berperilaku sama dengan Java tetapi jangan lupa bahwa semua kelas Anda atribut harus berupa pointer, Anda juga harus mengubah nilai pointer,akan lebih jelas dengan penjelasan kode.
Tapi ini bukan ide yang baik karena Anda akan berakhir dengan menulis banyak kode yang melibatkan pointer, yang rentan terhadap kebocoran memori dan jangan lupa untuk memanggil destruktor. Dan untuk menghindari hal ini c ++ memiliki copy constructor di mana Anda akan membuat memori baru ketika objek yang berisi pointer diteruskan ke argumen fungsi yang akan berhenti memanipulasi data objek lain, Java melewati nilai dan nilai referensi, sehingga tidak memerlukan copy constructor.
sumber
Ada tiga metode untuk meneruskan objek ke fungsi sebagai parameter:
Pergi melalui contoh berikut:
Keluaran:
sumber
Berikut ini adalah cara untuk meneruskan argumen / parameter agar berfungsi di C ++.
1. berdasarkan nilai.
2. dengan referensi.
3. oleh objek.
sumber