Saya bingung apa masalahnya jika konstruktor diwarisi dari kelas dasar. Cpp Primer Plus mengatakan,
Konstruktor berbeda dari metode kelas lain dalam hal mereka membuat objek baru, sedangkan metode lain dipanggil oleh objek yang ada . Ini adalah salah satu alasan konstruktor tidak diwariskan . Warisan berarti objek yang diturunkan dapat menggunakan metode kelas dasar, tetapi, dalam kasus konstruktor, objek tidak ada sampai setelah konstruktor menyelesaikan tugasnya.
Saya mengerti konstruktor dipanggil sebelum konstruksi objek selesai.
Bagaimana ini dapat menimbulkan masalah jika kelas anak mewarisi ( Dengan mewarisi maksud saya kelas anak dapat menimpa metode kelas induk dll. Tidak hanya mengakses metode kelas induk ) induk konstruktor?
Saya mengerti tidak ada keharusan secara eksplisit memanggil konstruktor dari dalam kode [belum saya sadari.] Kecuali saat membuat objek. Bahkan kemudian Anda dapat melakukannya dengan menggunakan beberapa mekanisme untuk memanggil konstuktor induk [Dalam cpp, menggunakan ::
atau menggunakan member initialiser list
, Dalam java menggunakan super
]. Di Jawa ada penegakan untuk menyebutnya di baris 1, saya mengerti itu adalah untuk memastikan objek induk dibuat terlebih dahulu dan kemudian konstruksi objek anak dilanjutkan.
Itu bisa menimpanya . Tetapi, saya tidak dapat menemukan situasi di mana ini dapat menimbulkan masalah. Jika anak mewarisi konstruktor orang tua, apa yang salah?
Begitu juga ini hanya untuk menghindari mewarisi fungsi yang tidak perlu. Atau ada yang lebih dari itu?
sumber
Jawaban:
Tidak boleh ada warisan konstruktor yang tepat di C ++, karena konstruktor dari kelas turunan perlu melakukan tindakan tambahan yang tidak harus dilakukan dan tidak diketahui oleh konstruktor kelas dasar. Tindakan tambahan ini adalah inisialisasi anggota data dari kelas turunan (dan dalam implementasi yang khas, juga mengatur vpointer untuk merujuk ke kelas turunan vtable).
Ketika sebuah kelas dikonstruksi, ada sejumlah hal yang selalu perlu terjadi: Konstruktor dari kelas dasar (jika ada) dan anggota langsung perlu dipanggil dan jika ada fungsi virtual, vpointer harus diatur dengan benar. . Jika Anda tidak menyediakan konstruktor untuk kelas Anda, maka kompiler akan membuat yang melakukan tindakan yang diperlukan dan tidak ada yang lain. Jika Anda melakukan memberikan konstruktor, tapi kehilangan beberapa tindakan yang diperlukan (misalnya, inisialisasi beberapa anggota), maka compiler akan secara otomatis menambahkan tindakan yang hilang untuk konstruktor Anda. Dengan cara ini, kompilator memastikan bahwa setiap kelas memiliki setidaknya satu konstruktor dan bahwa setiap konstruktor sepenuhnya menginisialisasi objek yang dibuatnya.
Dalam C ++ 11, bentuk 'konstruktor pewarisan' telah diperkenalkan di mana Anda dapat menginstruksikan kompiler untuk menghasilkan seperangkat konstruktor untuk Anda yang menggunakan argumen yang sama dengan konstruktor dari kelas dasar dan yang hanya meneruskan argumen tersebut ke kelas dasar.
Meskipun secara resmi disebut warisan, itu tidak benar-benar begitu karena masih ada fungsi spesifik kelas turunan. Sekarang hanya dihasilkan oleh kompiler alih-alih ditulis secara eksplisit oleh Anda.
Fitur ini berfungsi seperti ini:
Derived
sekarang memiliki dua konstruktor (tidak termasuk copy / pindah konstruktor). Satu yang mengambil int dan string dan yang hanya mengambil int.sumber
m()
asalnya, dan bagaimana itu akan berubah jika jenisnya misalnyaint
.using Base::Base
implisit? Ini akan memiliki konsekuensi besar jika saya lupa garis itu pada kelas turunan dan saya perlu mewarisi konstruktor di semua kelas turunanTidak jelas apa yang Anda maksud dengan "mewarisi konstruktor induk". Anda menggunakan kata ganti , yang menunjukkan Anda mungkin berpikir tentang konstruktor yang berperilaku seperti fungsi virtual polimorfik . Saya sengaja tidak menggunakan istilah "konstruktor virtual" karena itu adalah nama umum untuk pola kode di mana Anda sebenarnya memerlukan instance objek yang sudah ada untuk membuat yang lain.
Ada sedikit utilitas untuk konstruktor polimorfik di luar pola "konstruktor virtual", dan sulit untuk membuat skenario konkret di mana konstruktor polimorfik yang sebenarnya mungkin digunakan. Contoh yang sangat dibuat-buat yang sama sekali tidak valid C ++ :
Dalam hal ini konstruktor yang dipanggil tergantung pada jenis konkret variabel yang sedang dibangun atau ditugaskan. Sangat kompleks untuk mendeteksi selama parsing / pembuatan kode dan tidak memiliki utilitas: Anda tahu tipe konkret yang Anda buat dan Anda telah menulis konstruktor khusus untuk kelas turunan. Kode C ++ yang valid berikut melakukan hal yang persis sama, sedikit lebih pendek dan lebih eksplisit dalam hal apa yang dilakukannya:
Interpretasi kedua atau mungkin pertanyaan tambahan adalah - bagaimana jika konstruktor kelas Base secara otomatis hadir di semua kelas turunan kecuali disembunyikan secara eksplisit.
Anda harus menulis kode tambahan untuk menyembunyikan konstruktor induk yang tidak benar untuk digunakan dalam membangun kelas turunan. Ini bisa terjadi ketika kelas turunan mengkhususkan kelas dasar dengan cara parameter tertentu menjadi tidak relevan.
Contoh khas adalah persegi panjang dan kotak (Perhatikan bahwa kotak dan persegi panjang umumnya tidak dapat diganti Liskov sehingga bukan desain yang sangat bagus, tetapi menyoroti masalah ini).
Jika Square mewarisi konstruktor dua nilai Rectangle, Anda bisa membuat kotak dengan tinggi dan lebar yang berbeda ... Itu salah secara logis, jadi Anda ingin menyembunyikan konstruktor itu.
sumber
Mengapa konstruktor tidak diwariskan: jawabannya sangat sederhana: Konstruktor kelas dasar "membangun" kelas dasar dan konstruktor kelas mewarisi "membangun" kelas warisan. Jika kelas yang diwarisi akan mewarisi konstructer, konstruktor akan mencoba untuk membangun objek kelas tipe dasar dan Anda tidak akan bisa "membangun" objek kelas tipe yang diwarisi.
Yang seperti itu mengalahkan tujuan mewarisi kelas.
sumber
Masalah yang paling jelas dengan memungkinkan kelas turunan untuk menimpa konstruktor kelas dasar adalah bahwa pengembang kelas turunan sekarang bertanggung jawab untuk mengetahui bagaimana membangun kelas dasar (es). Apa yang terjadi ketika kelas turunan tidak membangun kelas dasar dengan benar?
Juga, prinsip Substitusi Liskov tidak lagi berlaku karena Anda tidak lagi dapat mengandalkan koleksi objek kelas dasar Anda agar kompatibel satu sama lain, karena tidak ada jaminan bahwa kelas dasar dibangun dengan benar atau kompatibel dengan tipe turunan lainnya.
Lebih rumit lagi ketika lebih dari 1 level warisan ditambahkan. Sekarang kelas turunan Anda perlu tahu bagaimana membangun semua kelas dasar ke atas rantai.
Lalu apa yang terjadi jika Anda menambahkan kelas dasar baru ke bagian atas hierarki warisan? Anda harus memperbarui semua konstruktor kelas turunan.
sumber
Konstruktor secara fundamental berbeda dari metode lain:
Jadi mengapa mereka tidak diwariskan? Jawaban sederhana: Karena selalu ada penggantian, baik dibuat atau ditulis secara manual.
Mengapa setiap kelas membutuhkan konstruktor? Itu adalah pertanyaan yang rumit dan saya percaya jawabannya tergantung pada kompiler. Ada sesuatu yang disebut sebagai konstruktor "sepele" di mana kompiler tidak mengamanatkan bahwa itu akan disebut tampaknya. Saya pikir itu adalah hal yang paling dekat dengan apa yang Anda maksud dengan pewarisan tetapi untuk tiga alasan yang disebutkan di atas saya pikir membandingkan konstruktor dengan metode normal tidak terlalu berguna. :)
sumber
Setiap kelas membutuhkan konstruktor, bahkan yang standar.
C ++ akan membuat konstruktor default untuk Anda kecuali jika Anda membuat konstruktor khusus.
Jika kelas dasar Anda menggunakan konstruktor khusus, Anda harus menulis konstruktor khusus pada kelas turunan meskipun keduanya sama dan membuat rantai kembali.
C ++ 11 memungkinkan Anda untuk menghindari duplikasi kode pada konstruktor menggunakan menggunakan :
Seperangkat konstruktor bawaan terdiri dari
Semua konstruktor yang diwariskan yang bukan konstruktor default atau salin / pindahkan konstruktor dan yang tandatangannya tidak cocok dengan konstruktor yang ditentukan pengguna di kelas turunan, secara implisit dinyatakan dalam kelas turunan. Parameter default tidak diwarisi
sumber
Anda dapat gunakan:
Apakah Anda bertanya mengapa Anda harus melakukan ini?
Sub-kelas dapat memiliki properti tambahan yang mungkin perlu diinisialisasi dalam konstruktor, atau mungkin menginisialisasi variabel kelas dasar dengan cara yang berbeda.
Bagaimana lagi Anda membuat objek sub-tipe?
sumber