Mengapa konstruktor tanpa parameter default hilang saat Anda membuatnya dengan parameter

161

Di C #, C ++ dan Java, saat Anda membuat parameter konstruktor, parameter tanpa parameter default hilang. Saya selalu menerima kenyataan ini, tetapi sekarang saya mulai bertanya-tanya mengapa.

Apa alasan perilaku ini? Apakah ini hanya "ukuran / dugaan keselamatan" yang mengatakan "Jika Anda telah membuat konstruktor sendiri, Anda mungkin tidak ingin yang implisit ini berkeliaran"? Atau apakah ia memiliki alasan teknis yang membuat mustahil bagi kompiler untuk menambahkan satu setelah Anda membuat konstruktor sendiri?

olagjo
sumber
7
Anda dapat menambahkan C ++ ke daftar bahasa yang memiliki perilaku ini.
Henk Holterman
18
Dan di C ++ sekarang Anda bisa mengatakan Foo() = default;untuk mendapatkan kembali default.
MSalters
1
Jika konstruktor Anda dengan parameter dapat memiliki argumen default untuk semua parameternya, maka itu akan bertentangan dengan konstruktor tanpa parameter bawaan, maka kebutuhan untuk menghapusnya saat Anda membuat parameter Anda sendiri.
Morwenn
3
Bayangkan hadir untuk debat ini antara pendiri kompiler pertama untuk membuat persyaratan konstruktor default dan diskusi panas yang akan diilhami.
kingdango
7
@HenkHolterman C + + tidak hanya orang lain dengan perilaku ini, tetapi pencetusnya, dan itu untuk memungkinkan kompatibilitas C, seperti yang dibahas oleh Stroustrup dalam Desain dan Evolusi C ++ dan seperti yang dirangkum dalam jawaban saya yang diperbarui.
Jon Hanna

Jawaban:

219

Tidak ada alasan bahwa kompiler tidak dapat menambahkan konstruktor jika Anda telah menambahkan konstruktor Anda sendiri - kompiler dapat melakukan hampir semua yang diinginkan! Namun, Anda harus melihat apa yang paling masuk akal:

  • Jika saya belum ditetapkan setiap konstruktor untuk kelas non-statis, saya kemungkinan besar ingin dapat instantiate kelas itu. Untuk memperbolehkannya, kompiler harus menambahkan konstruktor tanpa parameter, yang tidak akan berpengaruh kecuali untuk memungkinkan instantiasi. Ini berarti bahwa saya tidak harus memasukkan konstruktor kosong dalam kode saya hanya untuk membuatnya berfungsi.
  • Jika saya telah mendefinisikan konstruktor saya sendiri, terutama yang memiliki parameter, maka saya kemungkinan besar memiliki logika sendiri yang harus dijalankan untuk membuat kelas. Jika kompiler membuat konstruktor kosong tanpa parameter dalam kasus ini, itu akan memungkinkan seseorang untuk melompati logika yang telah saya tulis, yang mungkin menyebabkan kode saya rusak dalam semua cara. Jika saya ingin konstruktor kosong default dalam hal ini, saya perlu mengatakannya secara eksplisit.

Jadi, dalam setiap kasus, Anda dapat melihat bahwa perilaku kompiler saat ini paling masuk akal dalam hal menjaga maksud kode.

Dan Puzey
sumber
2
Saya pikir sisa jawaban Anda cukup banyak membuktikan bahwa kalimat pertama Anda salah.
Konrad Rudolph
76
@KonradRudolph, kalimat pertama mengatakan kompiler dapat menambahkan konstruktor dalam skenario ini - sisa jawabannya menjelaskan mengapa tidak (untuk bahasa yang ditentukan dalam pertanyaan)
JohnL
1
Baik tidak Jika kami mendesain bahasa OO dari awal, makna yang paling jelas dari tidak adanya konstruktor adalah, "Anda lalai menambahkan konstruktor yang memastikan kelas 'invarian" dan itu akan menimbulkan kesalahan kompilasi.
Jon Hanna
70

Ada tentu saja tidak ada alasan teknis mengapa bahasa memiliki harus dirancang dengan cara ini.

Ada empat opsi yang agak realistis yang bisa saya lihat:

  1. Tidak ada konstruktor default sama sekali
  2. Skenario saat ini
  3. Selalu menyediakan konstruktor default secara default, tetapi membiarkannya ditekan secara eksplisit
  4. Selalu memberikan konstruktor default tanpa membiarkannya ditekan

Opsi 1 agak menarik, karena semakin saya kode semakin jarang saya benar - benar menginginkan konstruktor tanpa parameter. Suatu hari saya harus menghitung seberapa sering saya benar-benar menggunakan konstruktor default ...

Opsi 2 Saya setuju.

Opsi 3 bertentangan dengan aliran Java dan C #, untuk sisa bahasa. Tidak pernah ada yang Anda "hapus" secara eksplisit, kecuali jika Anda secara eksplisit membuat hal-hal yang lebih pribadi daripada yang secara default di Jawa.

Opsi 4 mengerikan - Anda benar - benar ingin dapat memaksa konstruksi dengan parameter tertentu. Apa new FileStream()artinya?

Jadi pada dasarnya, jika Anda menerima premis bahwa menyediakan konstruktor default masuk akal sama sekali, saya percaya itu masuk akal untuk menekannya segera setelah Anda memberikan konstruktor sendiri.

Jon Skeet
sumber
1
Saya suka opsi 3, karena ketika saya menulis sesuatu, saya perlu lebih sering memiliki kedua jenis konstruktor kemudian hanya konstruktor dengan parameter. Jadi saya lebih suka sekali sehari membuat beberapa konstruktor tanpa parameter pribadi kemudian menulis 10 kali sehari konstruktor tanpa parameter. Tapi itu mungkin hanya saya, saya menulis banyak kelas seriaziable ...
Petr Mensik
8
@PetrMensik: Jika konstruktor tanpa parameter Anda benar-benar tidak perlu melakukan apa-apa, itu adalah satu-baris yang pasti tidak akan mengambil lebih banyak kode daripada pernyataan "penghapusan eksplisit".
Jon Skeet
2
@PetrMensik: Maaf, kesalahan saya, ya. Itu jelas kebalikan dari pengalaman saya - dan saya berpendapat bahwa memasukkan sesuatu secara otomatis adalah pilihan yang lebih berbahaya juga ... jika Anda secara tidak sengaja tidak mengecualikannya, Anda dapat mengacaukan invarian Anda, dll.
Jon Skeet
2
# 4 adalah kasus dengan C # structs, baik atau buruk.
Jay Bazuzi
4
Saya suka opsi 1 karena lebih mudah dimengerti. Tidak ada konstruktor "ajaib" yang tidak dapat Anda lihat dalam kode. Dengan opsi 1 kompiler harus memancarkan warnig ketika (atau bahkan mungkin tidak diizinkan?) Kelas non-statis tidak memiliki konstruktor instance. Bagaimana dengan: "Tidak ada konstruktor instance yang ditemukan untuk kelas <TYPE>. Apakah Anda bermaksud menyatakan kelas statis?"
Jeppe Stig Nielsen
19

Edit. Sebenarnya, sementara apa yang saya katakan dalam jawaban pertama saya valid, ini adalah alasan sebenarnya .:

Pada awalnya ada C. C tidak berorientasi objek (Anda dapat mengambil pendekatan OO, tetapi tidak membantu Anda atau menegakkan apa pun).

Lalu ada C With Classes, yang kemudian dinamai C ++. C ++ berorientasi objek, dan karena itu mendorong enkapsulasi, dan memastikan invarian objek - setelah konstruksi dan pada awal dan akhir metode apa pun, objek berada dalam keadaan valid.

Hal alami yang harus dilakukan dengan ini, adalah untuk menegakkan bahwa kelas harus selalu memiliki konstruktor untuk memastikannya dimulai dalam keadaan yang valid - jika konstruktor tidak harus melakukan apa pun untuk memastikan ini, maka konstruktor kosong akan mendokumentasikan fakta ini .

Tetapi tujuan dengan C ++ adalah agar kompatibel dengan C sampai pada titik bahwa sebanyak mungkin, semua program C yang valid juga merupakan program C ++ yang valid (tidak lagi sebagai tujuan aktif, dan evolusi C terpisah ke C ++ berarti ia tidak lagi berlaku ).

Salah satu efek dari ini adalah duplikasi fungsi antara structdan class. Yang pertama melakukan hal-hal dengan cara C (semuanya publik secara default) dan yang terakhir melakukan hal-hal dengan cara OO yang baik (semuanya pribadi secara default, pengembang secara aktif membuat publik apa yang mereka inginkan publik).

Lain adalah bahwa agar C struct, yang tidak bisa memiliki konstruktor karena C tidak memiliki konstruktor, untuk valid dalam C ++, maka harus ada makna untuk ini kepada cara C ++ memandangnya. Jadi, sementara tidak memiliki konstruktor akan bertentangan dengan praktik OO untuk secara aktif memastikan invarian, C ++ menganggap ini berarti bahwa ada konstruktor tanpa parameter default yang bertindak seperti memiliki tubuh kosong.

Semua C structssekarang valid C ++ structs, (yang berarti mereka sama dengan C ++ classesdengan semuanya - anggota dan warisan - publik) diperlakukan dari luar seolah-olah ia memiliki konstruktor tunggal tanpa parameter.

Namun jika Anda memasukkan konstruktor ke dalam classatau struct, maka Anda melakukan hal-hal dengan cara C ++ / OO daripada cara C, dan tidak ada kebutuhan untuk konstruktor default.

Karena berfungsi sebagai singkatan, orang-orang terus menggunakannya bahkan ketika kompatibilitas tidak mungkin terjadi (menggunakan fitur C ++ lainnya yang tidak ada di C).

Oleh karena itu ketika Java muncul (berdasarkan C ++ dalam banyak hal) dan kemudian C # (berdasarkan C ++ dan Java dengan cara yang berbeda), mereka mempertahankan pendekatan ini karena sesuatu yang mungkin sudah digunakan oleh pembuat kode.

Stroustrup menulis tentang ini dalam Bahasa Pemrograman C ++ dan lebih dari itu, dengan lebih fokus pada "mengapa" bahasa dalam Desain dan Evolusi C ++ .

=== Jawaban Asli ===

Katakanlah ini tidak terjadi.

Katakanlah saya tidak ingin konstruktor tanpa parameter, karena saya tidak dapat membuat kelas saya menjadi keadaan yang bermakna tanpa satu. Memang, ini adalah sesuatu yang dapat terjadi dengan structdi C # (tetapi jika Anda tidak dapat menggunakan semua-nol-dan-nulls yang berarti structdi C # Anda paling baik menggunakan optimasi yang tidak terlihat oleh publik, dan jika tidak memiliki desain cacat dalam menggunakan struct).

Untuk membuat kelas saya dapat melindungi invariannya, saya perlu removeDefaultConstructorkata kunci khusus . Paling tidak, saya harus membuat konstruktor tanpa parameter pribadi untuk memastikan tidak ada kode panggilan yang memanggil default.

Yang menyulitkan bahasa lagi. Lebih baik tidak melakukannya.

Secara keseluruhan, lebih baik tidak menganggap menambahkan konstruktor sebagai menghapus default, lebih baik untuk berpikir tidak memiliki konstruktor sama sekali sebagai gula sintaksis untuk menambahkan konstruktor tanpa parameter yang tidak melakukan apa-apa.

Jon Hanna
sumber
1
Dan lagi, saya melihat seseorang merasa ini adalah jawaban yang cukup buruk untuk memilihnya, tetapi tidak dapat diganggu untuk mencerahkan saya atau orang lain. Yang mungkin, Anda tahu, berguna.
Jon Hanna
Sama dengan jawaban saya. Yah saya tidak melihat ada yang salah di sini jadi +1 dari saya.
Botz3000
@ Botz3000 Saya tidak peduli dengan nilainya, tetapi jika mereka memiliki kritik, saya lebih suka membacanya. Tetap saja, hal itu membuat saya memikirkan sesuatu untuk ditambahkan di atas.
Jon Hanna
1
Dan lagi dengan down-voting tanpa ada penjelasan. Tolong, jika saya melewatkan sesuatu yang begitu jelas sehingga tidak memerlukan penjelasan, anggap saja saya bodoh dan tolong saya jelaskan juga.
Jon Hanna
1
@jogojapan saya bukan ahli juga, tetapi orang yang - menjadi orang yang membuat keputusan - menulis tentang hal itu, jadi saya tidak harus menjadi. Kebetulan, itu adalah buku yang sangat menarik; sedikit pada teknologi tingkat rendah dan banyak pada keputusan desain, dengan bit lucu seperti ucapan satu orang mengatakan Anda harus menyumbangkan ginjal sebelum mengusulkan fitur baru (Anda akan berpikir sangat keras, dan hanya melakukannya dua kali) tepat sebelum pidatonya memperkenalkan kedua templat, dan pengecualian.
Jon Hanna
13

Konstruktor tanpa parameter default ditambahkan jika Anda tidak melakukan apa pun untuk mengendalikan pembuatan objek. Setelah Anda membuat konstruktor tunggal untuk mengambil kendali, kompiler "mundur" dan membiarkan Anda memiliki kontrol penuh.

Jika tidak seperti ini, Anda perlu cara eksplisit menonaktifkan konstruktor default jika Anda hanya ingin objek dapat dibangun melalui konstruktor dengan parameter.

Anders Abel
sumber
Anda sebenarnya memiliki opsi itu. Membuat konstruktor tanpa parameter menjadi pribadi.
mw_21
5
Itu tidak sama. Metode kelas apa pun, termasuk metode statis, bisa menyebutnya. Saya lebih suka memilikinya sama sekali tidak ada.
Anders Abel
3

Ini adalah fungsi kenyamanan dari kompiler. Jika Anda mendefinisikan Konstruktor dengan parameter tetapi tidak mendefinisikan konstruktor tanpa parameter, kemungkinan Anda tidak ingin mengizinkan konstruktor tanpa parameter jauh lebih tinggi.

Ini adalah kasus untuk banyak objek yang tidak masuk akal untuk menginisialisasi dengan konstruktor kosong.

Jika tidak, Anda harus mendeklarasikan konstruktor tanpa parameter pribadi untuk setiap kelas yang ingin Anda batasi.

Menurut pendapat saya itu bukan gaya yang baik untuk memungkinkan konstruktor tanpa parameter untuk kelas yang membutuhkan parameter berfungsi.

mw_21
sumber
3

Saya pikir pertanyaannya harus sebaliknya: Mengapa Anda tidak perlu mendeklarasikan konstruktor default jika Anda belum mendefinisikan konstruktor lain?

Konstruktor wajib untuk kelas non-statis.
Jadi saya pikir jika Anda belum mendefinisikan konstruktor, konstruktor default yang dihasilkan hanya fitur yang nyaman dari kompiler C #, juga kelas Anda tidak akan valid tanpa konstruktor. Jadi tidak ada yang salah dengan menghasilkan konstruktor yang tidak melakukan apa-apa. Jelas terlihat lebih bersih daripada memiliki konstruktor kosong di sekitar.

Jika Anda telah mendefinisikan konstruktor, kelas Anda valid, jadi mengapa kompilator harus menganggap Anda menginginkan konstruktor default? Bagaimana jika Anda tidak menginginkannya? Menerapkan atribut untuk memberi tahu kompiler untuk tidak menghasilkan konstruktor default itu? Saya pikir itu bukan ide yang bagus.

Botz3000
sumber
1

Konstruktor default dapat dibangun hanya ketika kelas tidak memiliki konstruktor. Kompiler ditulis sedemikian rupa untuk menyediakan ini hanya sebagai mekanisme cadangan.

Jika Anda memiliki konstruktor berparameter, Anda mungkin tidak ingin objek dibuat menggunakan konstruktor default. Seandainya kompiler menyediakan konstruktor default, Anda harus menulis konstruktor no-arg dan menjadikannya pribadi untuk mencegah objek dibuat tanpa menggunakan argumen.

Juga, akan ada peluang lebih tinggi Anda lupa menonaktifkan, atau 'memprivatisasi' konstruktor default, dan dengan demikian menyebabkan kesalahan fungsional potensial yang sulit ditangkap.

Dan sekarang Anda harus secara eksplisit mendefinisikan konstruktor no-arg jika Anda ingin objek dibuat dengan cara default atau dengan melewatkan parameter. Ini sangat diperiksa, dan kompiler mengeluh sebaliknya, dengan demikian memastikan tidak ada celah di sini.

abu
sumber
1

Premis

Perilaku ini dapat dilihat sebagai perpanjangan alami dari keputusan untuk kelas untuk memiliki konstruktor tanpa parameter publik default . Berdasarkan pertanyaan yang diajukan, kami mengambil keputusan ini sebagai premis dan menganggap bahwa kami tidak mempertanyakannya dalam hal ini.

Cara untuk Menghapus Konstruktor Default

Oleh karena itu harus ada cara untuk menghapus konstruktor tanpa parameter publik default. Penghapusan ini dapat dilakukan dengan cara-cara berikut:

  1. Deklarasikan konstruktor tanpa parameter non-publik
  2. Secara otomatis menghapus konstruktor tanpa parameter ketika konstruktor dengan parameter dideklarasikan
  3. Beberapa kata kunci / atribut untuk menunjukkan kepada kompiler untuk menghapus konstruktor tanpa parameter (cukup canggung sehingga mudah untuk dikesampingkan)

Memilih Solusi Terbaik

Sekarang kita bertanya pada diri sendiri: Jika tidak ada konstruktor tanpa parameter, apa yang harus diganti? dan Di bawah jenis skenario apa kita ingin menghapus konstruktor tanpa parameter publik default?

Segalanya mulai jatuh pada tempatnya. Pertama, harus diganti dengan konstruktor dengan parameter, atau dengan konstruktor non-publik. Kedua, skenario di mana Anda tidak ingin konstruktor tanpa parameter adalah:

  1. Kami tidak ingin kelas dipakai sama sekali, atau kami ingin mengontrol visibilitas konstruktor: nyatakan konstruktor non-publik
  2. Kami ingin memaksa parameter disediakan pada konstruksi: menyatakan konstruktor dengan parameter

Kesimpulan

Di sana kita memilikinya - persis dua cara yang C #, C ++ dan Java memungkinkan penghapusan konstruktor tanpa parameter publik default.

Zaid Masud
sumber
Jelas terstruktur dan mudah diikuti, +1. Tetapi mengenai (3.) di atas: Saya tidak berpikir kata kunci khusus untuk penghapusan konstruktor adalah ide yang aneh, dan sebenarnya C ++ 11 telah diperkenalkan = deleteuntuk tujuan ini.
jogojapan
1

Saya pikir ini ditangani oleh kompiler. Jika Anda membuka .netperakitan di ILDASMAnda akan melihat konstruktor default, bahkan jika itu tidak ada dalam kode. Jika Anda mendefinisikan konstruktor berparameter, konstruktor default tidak akan terlihat.

Sebenarnya ketika Anda mendefinisikan kelas (non-statis), kompiler menyediakan fitur ini dengan berpikir bahwa Anda hanya akan membuat instance. Dan jika Anda ingin operasi tertentu dilakukan, Anda pasti akan memiliki konstruktor sendiri.

Teknologi PQube
sumber
0

Karena ketika Anda tidak mendefinisikan konstruktor, kompiler secara otomatis menghasilkan konstruktor untuk Anda yang tidak mengambil argumen apa pun. Ketika Anda menginginkan sesuatu yang lebih dari konstruktor, Anda menumpanginya. Ini BUKAN fungsi overloading. Jadi satu-satunya konstruktor yang dilihat kompiler adalah konstruktor Anda yang mengambil argumen. Untuk mengatasi masalah ini, Anda dapat memberikan nilai default jika konstruktor dilewatkan tanpa nilai.

Vivek Pradhan
sumber
0

Kelas membutuhkan konstruktor. Ini adalah persyaratan wajib.

  • Jika Anda tidak membuat satu, konstruktor tanpa parameter akan diberikan kepada Anda secara otomatis.
  • Jika Anda tidak menginginkan konstruktor tanpa parameter, maka Anda harus membuatnya sendiri.
  • Jika Anda membutuhkan keduanya, konstruktor tanpa parameter dan konstruktor berbasis parameter, Anda dapat menambahkannya secara manual.

Saya akan menjawab pertanyaan Anda dengan yang lain, mengapa kami ingin selalu menjadi konstruktor tanpa parameter default? ada kasus di mana ini tidak diinginkan, sehingga pengembang memiliki kontrol untuk menambah atau menghapusnya sesuai kebutuhan.

Jaider
sumber