Anda masuk ke hipotesis dengan jawaban ini, jadi saya akan mencoba untuk membuat penjelasan yang lebih sederhana, lebih membumi untuk kejelasan demi.
Hubungan dasar desain berorientasi objek adalah dua: IS-A dan HAS-A. Saya tidak mengada-ada. Itulah sebutan mereka.
IS-A menunjukkan bahwa objek tertentu mengidentifikasi sebagai kelas yang di atasnya dalam hirarki kelas. Objek pisang adalah objek buah jika merupakan subkelas dari kelas buah. Ini berarti bahwa di mana pun kelas buah dapat digunakan, pisang dapat digunakan. Ini bukan refleksif. Anda tidak dapat mengganti kelas dasar untuk kelas tertentu jika kelas spesifik itu dipanggil.
Has-a menunjukkan bahwa objek adalah bagian dari kelas komposit dan ada hubungan kepemilikan. Ini berarti dalam C ++ bahwa itu adalah objek anggota dan dengan demikian tanggung jawab berada pada kelas pemilik untuk membuangnya atau melepaskan kepemilikan sebelum merusak dirinya sendiri.
Kedua konsep ini lebih mudah diwujudkan dalam bahasa pewarisan tunggal daripada dalam model pewarisan berganda seperti c ++, tetapi aturan dasarnya sama. Komplikasi muncul ketika identitas kelas ambigu, seperti meneruskan pointer kelas Banana ke fungsi yang mengambil pointer kelas Fruit.
Fungsi virtual adalah, pertama, hal run-time. Ini adalah bagian dari polimorfisme karena digunakan untuk memutuskan fungsi mana yang akan dijalankan pada saat dipanggil dalam program yang sedang berjalan.
Kata kunci virtual adalah arahan kompiler untuk mengikat fungsi dalam urutan tertentu jika ada ambiguitas tentang identitas kelas. Fungsi virtual selalu dalam kelas induk (sejauh yang saya tahu) dan menunjukkan kepada kompiler bahwa pengikatan fungsi anggota dengan nama mereka harus dilakukan dengan fungsi subkelas terlebih dahulu dan fungsi kelas induk setelahnya.
Kelas Buah dapat memiliki warna fungsi virtual () yang mengembalikan "NONE" secara default. Fungsi warna kelas Pisang () mengembalikan "YELLOW" atau "BROWN".
Tetapi jika fungsi yang mengambil pointer Buah memanggil warna () pada kelas Pisang yang dikirim kepadanya - fungsi warna () mana yang dipanggil? Fungsi biasanya akan memanggil Buah :: warna () untuk objek Buah.
Itu akan 99% dari waktu tidak menjadi apa yang dimaksudkan. Tetapi jika Fruit :: color () dinyatakan virtual maka Banana: color () akan dipanggil untuk objek karena fungsi color () yang benar akan terikat ke pointer Fruit pada saat panggilan. Runtime akan memeriksa objek apa yang ditunjuk oleh pointer karena itu ditandai virtual dalam definisi kelas Buah.
Ini berbeda dari mengesampingkan fungsi dalam subkelas. Dalam hal itu, pointer Buah akan memanggil Buah :: warna () jika yang diketahuinya adalah pointer IS-A ke Buah.
Jadi sekarang muncul ide "fungsi virtual murni". Ini adalah ungkapan yang agak disayangkan karena kemurnian tidak ada hubungannya dengan itu. Ini berarti bahwa ini dimaksudkan agar metode kelas dasar tidak pernah dipanggil. Memang fungsi virtual murni tidak bisa disebut. Namun itu masih harus didefinisikan. Tanda tangan fungsi harus ada. Banyak coder membuat implementasi kosong {} untuk kelengkapan, tetapi kompiler akan menghasilkan satu secara internal jika tidak. Dalam hal ini ketika fungsi dipanggil bahkan jika pointer ke Buah, Banana :: color () akan dipanggil karena hanya implementasi warna () yang ada.
Sekarang bagian terakhir dari teka-teki: konstruktor dan destruktor.
Konstruktor virtual murni ilegal, sepenuhnya. Itu baru saja keluar.
Tetapi destruktor virtual murni berfungsi jika Anda ingin melarang pembuatan instance kelas dasar. Hanya sub-kelas yang dapat dipakai jika destruktor dari kelas dasar adalah virtual murni. konvensi adalah untuk menetapkannya ke 0.
virtual ~Fruit() = 0; // pure virtual
Fruit::~Fruit(){} // destructor implementation
Anda harus membuat implementasi dalam hal ini. Kompiler tahu ini adalah apa yang Anda lakukan dan memastikan Anda melakukannya dengan benar, atau mengeluh bahwa ia tidak dapat menautkan ke semua fungsi yang diperlukan untuk dikompilasi. Kesalahan bisa membingungkan jika Anda tidak berada di jalur yang benar tentang bagaimana Anda memodelkan hierarki kelas Anda.
Jadi Anda dilarang dalam hal ini untuk membuat contoh Buah, tetapi diizinkan untuk membuat contoh Pisang.
Panggilan untuk menghapus pointer Buah yang menunjuk ke instance Banana akan memanggil Banana :: ~ Banana () terlebih dahulu dan kemudian memanggil Fuit :: ~ Fruit (), selalu. Karena bagaimanapun caranya, ketika Anda memanggil destruktor subclass, destructor kelas dasar harus mengikuti.
Apakah ini model yang buruk? Ini lebih rumit pada tahap desain, ya, tetapi dapat memastikan bahwa penghubungan yang benar dilakukan pada saat run-time dan bahwa fungsi subclass dilakukan di mana ada ambiguitas untuk tepatnya subclass mana yang sedang diakses.
Jika Anda menulis C ++ sehingga Anda hanya membagikan pointer kelas yang tepat tanpa pointer umum atau ambigu, maka fungsi virtual tidak benar-benar diperlukan. Tetapi jika Anda memerlukan fleksibilitas jenis waktu berjalan (seperti pada Apple Banana Orange ==> Buah) fungsi menjadi lebih mudah dan lebih fleksibel dengan kode yang tidak terlalu banyak. Anda tidak lagi harus menulis fungsi untuk setiap jenis buah, dan Anda tahu bahwa setiap buah akan merespons warna () dengan fungsi yang benar.
Saya harap penjelasan yang bertele-tele ini memperkuat konsep daripada membingungkan hal-hal. Ada banyak contoh bagus di luar sana untuk dilihat, dan melihat cukup dan benar-benar menjalankannya dan mengacaukannya dan Anda akan mendapatkannya.