Pertanyaan Kapan mengembalikan pointer, skalar, dan referensi di C ++?


Saya pindah dari Java ke C ++ dan saya agak bingung dengan fleksibilitas bahasa. Satu titik adalah bahwa ada tiga cara untuk menyimpan objek: Penunjuk, referensi, dan skalar (menyimpan objek itu sendiri jika saya memahaminya dengan benar).

Saya cenderung menggunakan referensi jika memungkinkan, karena itu sedekat mungkin dengan Java. Dalam beberapa kasus, misalnya getter untuk atribut turunan, ini tidak mungkin:

MyType &MyClass::getSomeAttribute() {
    MyType t;
    return t;
}

Ini tidak dikompilasi, karena t hanya ada dalam lingkup getSomeAttribute() dan jika saya kembali referensi ke itu, itu akan menunjukkan tempat sebelum klien dapat menggunakannya.

Oleh karena itu saya pergi dengan dua opsi:

  1. Kembalikan pointer
  2. Kembalikan skalar

Mengembalikan penunjuk akan terlihat seperti ini:

MyType *MyClass::getSomeAttribute() {
    MyType *t = new MyType;
    return t;
}

Ini berfungsi, tetapi klien harus memeriksa pointer ini NULL agar benar-benar yakin, sesuatu yang tidak perlu dengan referensi. Masalah lainnya adalah pemanggil harus memastikan hal itu t Deallocated, saya lebih suka tidak berurusan dengan itu jika saya bisa menghindarinya.

Alternatifnya adalah mengembalikan objek itu sendiri (skalar):

MyType MyClass::getSomeAttribute() {
    MyType t;
    return t;
}

Itu cukup mudah dan tepat seperti yang saya inginkan dalam kasus ini: Rasanya seperti referensi dan tidak boleh nol. Jika objek berada di luar ruang lingkup kode klien, itu dihapus. Sangat berguna. Namun, saya jarang melihat ada yang melakukan itu, apakah ada alasan untuk itu? Adakah masalah kinerja jika saya mengembalikan skalar bukan penunjuk atau referensi?

Apa pendekatan paling umum / elegan untuk menangani masalah ini?


32
2017-08-07 18:07


asal


Jawaban:


Kembalikan berdasarkan nilai. Compiler dapat mengoptimalkan salinan, sehingga hasil akhirnya adalah yang Anda inginkan. Objek dibuat, dan dikembalikan ke pemanggil.

Saya pikir alasan mengapa Anda jarang melihat orang melakukan ini adalah karena Anda melihat kode C ++ yang salah. ;) Kebanyakan orang yang datang dari Jawa merasa tidak nyaman melakukan sesuatu seperti ini, jadi mereka menelepon new seluruh tempat. Dan kemudian mereka mendapatkan kebocoran memori di semua tempat, harus memeriksa NULL dan semua masalah lain yang dapat menyebabkan. :)

Mungkin juga perlu menunjukkan bahwa referensi C ++ memiliki sangat sedikit kesamaan dengan referensi Java. Referensi di Java jauh lebih mirip dengan penunjuk (bisa diulang, atau disetel ke NULL). Bahkan satu-satunya perbedaan nyata adalah bahwa pointer dapat menunjuk ke nilai sampah juga (jika tidak terinisialisasi, atau menunjuk ke objek yang telah keluar dari ruang lingkup), dan bahwa Anda dapat melakukan penunjuk aritmatika pada pointer ke larik. Referensi C ++ adalah alias untuk suatu objek. Referensi Java tidak berperilaku seperti itu.


24
2017-08-07 18:11



Cukup sederhana, hindari penggunaan pointer dan alokasi dinamis oleh new sedapat mungkin. Gunakan nilai, referensi, dan objek yang dialokasikan secara otomatis. Tentu saja Anda tidak dapat selalu menghindari alokasi dinamis, tetapi itu harus menjadi pilihan terakhir, bukan yang pertama.


3
2017-08-07 18:12



Mengembalikan nilai dapat memperkenalkan hukuman kinerja karena ini berarti objek perlu disalin. Jika itu adalah objek besar, seperti daftar, operasi itu mungkin sangat mahal.

Tapi kompiler modern sangat bagus dalam membuat ini tidak terjadi. Standar C ++ secara eksplisit menyatakan bahwa kompiler diperbolehkan untuk mem-copy dalam keadaan tertentu. Contoh khusus yang akan relevan dalam kode contoh yang Anda berikan disebut 'pengoptimalan nilai balik'.

Secara pribadi, saya kembali dengan referensi (biasanya const) ketika saya mengembalikan variabel anggota, dan mengembalikan semacam objek pointer pintar dari beberapa jenis (sering ::std::auto_ptr) ketika saya perlu mengalokasikan sesuatu secara dinamis. Kalau tidak, saya kembali dengan nilai.

Saya juga sangat sering memilikinya const parameter referensi, dan ini sangat umum di C ++. Ini adalah cara melewatkan parameter dan mengatakan "fungsi tidak diizinkan untuk menyentuh ini". Pada dasarnya parameter read-only. Seharusnya hanya digunakan untuk objek yang lebih kompleks daripada satu bilangan bulat atau pointer sekalipun.

Saya pikir satu perubahan besar dari Java adalah itu const penting dan sangat sering digunakan. Belajar memahami dan menjadikannya teman Anda.

Saya juga berpikir jawaban Neil benar dalam menyatakan bahwa menghindari alokasi dinamis bila memungkinkan adalah ide yang bagus. Anda tidak boleh terlalu banyak mengubah desain untuk mewujudkannya, tetapi Anda harus memilih pilihan desain yang tidak harus terjadi.


2
2017-08-07 18:20



Mengembalikan nilai adalah hal yang biasa dilakukan di C ++. Namun, ketika Anda melewati suatu objek, Anda melewati referensi.

Contoh

 main()
   {

       equity trader;

       isTraderAllowed(trader);

       ....
    }

    bool isTraderAllowed(const equity& trdobj)
    {
             ... // Perform your function routine here.
    }

Di atas adalah contoh sederhana untuk mengirimkan objek dengan referensi. Pada kenyataannya, Anda akan memiliki metode yang disebut isTraderAllowed untuk kelas ekuitas, tapi saya menunjukkan kepada Anda penggunaan nyata lewat referensi.


0
2017-08-07 18:19



Sebuah titik tentang melewati nilai atau referensi:
Mempertimbangkan optimisasi, dengan asumsi fungsi adalah Di barisanJika parameternya dinyatakan sebagai "const DataType objectName", maka DataType bisa menjadi sesuatu yang primitif, tidak ada salinan objek yang akan terlibat; dan jika parameternya dideklarasikan sebagai "const DataType & objectName" atau "DataType & objectName", lagi-lagi DataType bisa berupa apa pun yang primitif, tidak ada alamat atau penunjuk yang akan terlibat. Dalam kedua kasus sebelumnya, argumen input digunakan langsung dalam kode assembly.

Sebuah titik mengenai referensi:
Referensi tidak selalu berupa pointer, sebagai contoh ketika Anda memiliki kode berikut dalam tubuh suatu fungsi, referensi bukan penunjuk:

int adad=5;
int & reference=adad;  

Sebuah titik tentang pengembalian berdasarkan nilai:
karena beberapa orang telah menyebutkan, menggunakan kompilator yang baik dengan kemampuan pengoptimalan, mengembalikan nilai dari jenis apa pun tidak akan menyebabkan salinan tambahan.

Sebuah titik tentang kembali dengan referensi:
Dalam kasus Di barisan fungsi dan optimasi, kembali dengan referensi tidak akan melibatkan pengambilan alamat atau penunjuk.


0
2017-08-07 20:10