Mengapa multiple inheritance dimungkinkan di C ++, tetapi tidak di C #?
Saya pikir (tanpa referensi keras), bahwa di Jawa mereka ingin membatasi ekspresi bahasa untuk membuat bahasa lebih mudah dipelajari dan karena kode yang menggunakan banyak pewarisan lebih sering terlalu rumit untuk kebaikannya sendiri daripada tidak. Dan karena multiple multiple inheritance jauh lebih rumit untuk diimplementasikan, sehingga ia menyederhanakan banyak mesin virtual (multiple inheritance berinteraksi secara buruk dengan pengumpul sampah, karena itu membutuhkan menjaga pointer ke tengah objek (di awal pangkalan) )
Dan ketika mendesain C # saya pikir mereka melihat Java, melihat bahwa multiple inheritance penuh memang tidak ketinggalan dan dipilih untuk menjaga hal-hal yang sederhana juga.
Bagaimana C ++ menyelesaikan ambiguitas tanda tangan metode identik yang diwarisi dari beberapa kelas dasar?
Itu tidak tidak . Ada sintaks untuk memanggil metode kelas dasar dari basis spesifik secara eksplisit, tetapi tidak ada cara untuk menimpa hanya satu dari metode virtual dan jika Anda tidak menimpa metode dalam subkelas, tidak mungkin untuk memanggilnya tanpa menentukan basis kelas.
Dan mengapa desain yang sama tidak dimasukkan ke dalam C #?
Tidak ada yang perlu digabungkan.
Karena Giorgio menyebutkan metode ekstensi antarmuka dalam komentar, saya akan menjelaskan apa itu mixin dan bagaimana mereka diimplementasikan dalam berbagai bahasa.
Antarmuka di Java dan C # terbatas hanya untuk mendeklarasikan metode. Tetapi metode harus diterapkan di setiap kelas yang mewarisi antarmuka. Namun ada kelas besar antarmuka, di mana akan berguna untuk memberikan implementasi standar dari beberapa metode dalam hal yang lain. Contoh umum dapat dibandingkan (dalam bahasa pseudo):
mixin IComparable {
public bool operator<(IComparable r) = 0;
public bool operator>(IComparable r) { return r < this; }
public bool operator<=(IComparable r) { return !(r < this); }
public bool operator>=(IComparable r) { return !(r > this); }
public bool operator==(IComparable r) { return !(r < this) && !(r > this); }
public bool operator!=(IComparable r) { return r < this || r > this; }
};
Perbedaan dari kelas penuh adalah ini tidak dapat berisi data anggota. Ada beberapa opsi untuk mengimplementasikan ini. Jelas banyak warisan adalah satu. Tetapi multiple inheritance agak rumit untuk diimplementasikan. Tapi itu tidak benar-benar dibutuhkan di sini. Sebagai gantinya, banyak bahasa mengimplementasikan ini dengan memisahkan mixin dalam sebuah antarmuka, yang diimplementasikan oleh kelas dan repositori implementasi metode, yang disuntikkan ke dalam kelas itu sendiri atau kelas dasar menengah dihasilkan dan mereka ditempatkan di sana. Ini diimplementasikan di Ruby dan D , akan diimplementasikan di Java 8 dan dapat diimplementasikan secara manual di C ++ menggunakan pola templat yang anehnya berulang . Di atas, dalam bentuk CRTP, terlihat seperti:
template <typename Derived>
class IComparable {
const Derived &_d() const { return static_cast<const Derived &>(*this); }
public:
bool operator>(const IComparable &r) const { r._d() < _d(); }
bool operator<=(const IComparable &r) const { !(r._d() < _d(); }
...
};
dan digunakan seperti:
class Concrete : public IComparable<Concrete> { ... };
Ini tidak memerlukan apa pun untuk dinyatakan virtual seperti yang akan dilakukan oleh kelas dasar biasa, jadi jika antarmuka yang digunakan dalam templat membiarkan opsi pengoptimalan yang bermanfaat terbuka. Perhatikan, bahwa dalam C ++ ini mungkin masih akan diwariskan sebagai induk kedua, tetapi dalam bahasa yang tidak memungkinkan banyak pewarisan itu dimasukkan ke dalam rantai pewarisan tunggal, jadi lebih seperti
template <typename Derived, typename Base>
class IComparable : public Base { ... };
class Concrete : public IComparable<Concrete, Base> { ... };
Implementasi kompiler mungkin atau mungkin tidak menghindari pengiriman virtual.
Implementasi yang berbeda dipilih dalam C #. Dalam C # implementasinya adalah metode statis dari kelas yang benar-benar terpisah dan sintaks pemanggilan metode secara tepat ditafsirkan oleh kompiler jika metode nama yang diberikan tidak ada, tetapi "metode ekstensi" didefinisikan. Ini memiliki keuntungan bahwa metode ekstensi dapat ditambahkan ke kelas yang sudah dikompilasi dan kelemahan bahwa metode tersebut tidak dapat ditimpa misalnya untuk menyediakan versi yang dioptimalkan.