Saya tahu judulnya terdengar akrab karena ada banyak pertanyaan serupa, tetapi saya meminta aspek masalah yang berbeda (saya tahu perbedaan antara memiliki barang-barang di tumpukan dan meletakkannya di tumpukan).
Di Jawa saya selalu bisa mengembalikan referensi ke objek "lokal"
public Thing calculateThing() {
Thing thing = new Thing();
// do calculations and modify thing
return thing;
}
Di C ++, untuk melakukan sesuatu yang serupa saya punya 2 pilihan
(1) Saya dapat menggunakan referensi kapan pun saya perlu "mengembalikan" suatu objek
void calculateThing(Thing& thing) {
// do calculations and modify thing
}
Kemudian gunakan seperti ini
Thing thing;
calculateThing(thing);
(2) Atau saya dapat mengembalikan pointer ke objek yang dialokasikan secara dinamis
Thing* calculateThing() {
Thing* thing(new Thing());
// do calculations and modify thing
return thing;
}
Kemudian gunakan seperti ini
Thing* thing = calculateThing();
delete thing;
Menggunakan pendekatan pertama saya tidak perlu membebaskan memori secara manual, tetapi bagi saya itu membuat kode sulit dibaca. Masalah dengan pendekatan kedua adalah, saya harus ingat delete thing;
, yang tidak terlihat bagus. Saya tidak ingin mengembalikan nilai yang disalin karena tidak efisien (saya pikir), jadi inilah pertanyaannya
- Apakah ada solusi ketiga (yang tidak perlu menyalin nilainya)?
- Apakah ada masalah jika saya tetap pada solusi pertama?
- Kapan dan mengapa saya harus menggunakan solusi kedua?
sumber
Jawaban:
Buktikan itu.
Cari RVO dan NRVO, dan dalam C ++ 0x semantik bergerak. Dalam kebanyakan kasus di C ++ 03, parameter keluar hanyalah cara yang baik untuk membuat kode Anda jelek, dan di C ++ 0x Anda sebenarnya akan melukai diri sendiri dengan menggunakan parameter keluar.
Cukup tulis kode bersih, kembalikan dengan nilai. Jika kinerja adalah masalah, buat profil itu (berhenti menebak), dan temukan apa yang dapat Anda lakukan untuk memperbaikinya. Kemungkinan tidak akan mengembalikan sesuatu dari fungsi.
Yang mengatakan, jika Anda sudah mati menulis seperti itu, Anda mungkin ingin melakukan parameter keluar. Ini menghindari alokasi memori dinamis, yang lebih aman dan umumnya lebih cepat. Itu memang mengharuskan Anda memiliki beberapa cara untuk membangun objek sebelum memanggil fungsi, yang tidak selalu masuk akal untuk semua objek.
Jika Anda ingin menggunakan alokasi dinamis, hal yang paling tidak bisa dilakukan adalah memasukkannya ke dalam smart pointer. (Lagipula ini harus dilakukan setiap saat) Maka Anda tidak perlu khawatir untuk menghapus apa pun, hal-hal itu aman dari pengecualian, dll. Satu-satunya masalah adalah kemungkinannya lebih lambat daripada mengembalikan nilainya!
sumber
Cukup buat objek dan kembalikan
Saya pikir Anda akan melakukan kebaikan jika Anda lupa tentang optimasi dan hanya menulis kode yang dapat dibaca (Anda harus menjalankan profiler nanti - tetapi jangan pra-optimalkan).
sumber
Thing thing();
mendeklarasikan fungsi lokal dan mengembalikan aThing
.Cukup kembalikan objek seperti ini:
Ini akan meminta copy constructor pada Things, jadi Anda mungkin ingin melakukan implementasi Anda sendiri untuk itu. Seperti ini:
Ini mungkin melakukan sedikit lebih lambat, tetapi mungkin tidak menjadi masalah sama sekali.
Memperbarui
Compiler mungkin akan mengoptimalkan panggilan ke copy constructor, sehingga tidak akan ada overhead tambahan. (Seperti dreamlax ditunjukkan dalam komentar).
sumber
Thing thing();
mendeklarasikan fungsi lokal yang mengembalikan aThing
, juga, standar memungkinkan kompiler untuk menghilangkan copy constructor dalam kasus yang Anda sajikan; setiap kompiler modern mungkin akan melakukannya.Apakah Anda mencoba menggunakan smart pointer (jika benda itu benar-benar benda besar dan berat), seperti auto_ptr:
sumber
auto_ptr
s sudah usang; gunakanshared_ptr
atauunique_ptr
sebagai gantinya.Salah satu cara cepat untuk menentukan apakah copy constructor dipanggil adalah dengan menambahkan logging ke copy constructor kelas Anda:
Panggilan
someFunction
; jumlah baris "Copy constructor dipanggil" yang akan Anda dapatkan akan bervariasi antara 0, 1, dan 2. Jika Anda tidak mendapatkannya, maka kompiler Anda telah mengoptimalkan nilai pengembalian (yang diizinkan untuk dilakukan). Jika Anda tidak mendapatkan 0, dan pembuat salinan Anda sangat mahal, maka cari cara alternatif untuk mengembalikan instance dari fungsi Anda.sumber
Pertama Anda memiliki kesalahan dalam kode, maksud Anda miliki
Thing *thing(new Thing());
, dan hanyareturn thing;
.shared_ptr<Thing>
. Lakukan itu karena itu adalah pointer. Ini akan dihapus untuk Anda ketika referensi terakhir untuk yangThing
terkandung keluar dari ruang lingkup.sumber
Saya yakin seorang ahli C ++ akan datang dengan jawaban yang lebih baik, tetapi secara pribadi saya suka pendekatan kedua. Menggunakan smart pointer membantu dengan masalah lupa
delete
dan seperti yang Anda katakan, itu terlihat lebih bersih daripada harus membuat objek sebelumnya (dan masih harus menghapusnya jika Anda ingin mengalokasikannya di heap).sumber