Saya sedang belajar C ++ dan saya baru saja masuk ke fungsi virtual.
Dari apa yang saya baca (dalam buku dan online), fungsi virtual adalah fungsi dalam kelas dasar yang dapat Anda timpa dalam kelas turunan.
Tetapi sebelumnya dalam buku ini, ketika belajar tentang pewarisan dasar, saya bisa mengesampingkan fungsi dasar di kelas turunan tanpa menggunakan virtual
.
Jadi apa yang saya lewatkan di sini? Saya tahu ada lebih banyak fungsi virtual, dan sepertinya penting jadi saya ingin menjelaskan apa itu sebenarnya. Saya tidak dapat menemukan jawaban langsung secara online.
c++
virtual-functions
Jake Wilson
sumber
sumber
Jawaban:
Berikut adalah cara saya memahami bukan hanya
virtual
fungsi apa , tetapi mengapa itu diperlukan:Katakanlah Anda memiliki dua kelas ini:
Dalam fungsi utama Anda:
Sejauh ini bagus, bukan? Hewan makan makanan generik, kucing makan tikus, semuanya tanpa
virtual
.Mari kita ubah sedikit sekarang sehingga
eat()
dipanggil melalui fungsi antara (fungsi sepele hanya untuk contoh ini):Sekarang fungsi utama kami adalah:
Uh oh ... kami melewati Kucing
func()
, tetapi tidak mau makan tikus. Haruskah Anda kelebihanfunc()
sehingga dibutuhkanCat*
? Jika Anda harus mendapatkan lebih banyak hewan dari Hewan, mereka semua akan membutuhkannya sendirifunc()
.Solusinya adalah membuat
eat()
dariAnimal
kelas fungsi virtual:Utama:
Selesai
sumber
virtual
memperkenalkan beberapa binding dinamis vs statis dan ya itu aneh jika Anda berasal dari bahasa seperti Java.Tanpa "virtual" Anda mendapatkan "ikatan awal". Implementasi metode mana yang digunakan akan diputuskan pada waktu kompilasi berdasarkan jenis pointer yang Anda panggil.
Dengan "virtual" Anda mendapatkan "keterlambatan mengikat". Implementasi metode mana yang digunakan akan diputuskan pada saat run time berdasarkan jenis objek runcing - apa yang awalnya dibangun sebagai. Ini belum tentu apa yang Anda pikirkan berdasarkan jenis pointer yang menunjuk ke objek itu.
EDIT - lihat pertanyaan ini .
Juga - tutorial ini mencakup pengikatan awal dan akhir dalam C ++.
sumber
main
fungsi dll. Pointer-to-diturunkan secara implisit dilemparkan ke pointer-to-base (lebih khusus secara implisit dilemparkan ke yang lebih umum). Visa-versa Anda memerlukan pemeran eksplisit, biasanya adynamic_cast
. Hal lain - sangat rentan terhadap perilaku yang tidak terdefinisi jadi pastikan Anda tahu apa yang Anda lakukan. Sepengetahuan saya, ini tidak berubah sejak sebelum bahkan C ++ 98.Anda membutuhkan setidaknya 1 level warisan dan orang yang tertunduk untuk menunjukkannya. Ini adalah contoh yang sangat sederhana:
sumber
Anda memerlukan metode virtual untuk downcasting yang aman , kesederhanaan dan keringkasan .
Itulah yang dilakukan metode virtual: mereka ditelusuri dengan aman, dengan kode yang tampaknya sederhana dan ringkas, menghindari gips manual yang tidak aman dalam kode yang lebih rumit dan bertele-tele yang seharusnya Anda miliki.
Metode non-virtual binding penjilidan statis
Kode berikut sengaja "salah". Itu tidak menyatakan
value
metode sebagaivirtual
, dan karena itu menghasilkan hasil "salah" yang tidak diinginkan, yaitu 0:Dalam baris yang dikomentari sebagai "buruk"
Expression::value
metode ini disebut, karena tipe yang dikenal secara statis (tipe yang dikenal pada waktu kompilasi) adalahExpression
, danvalue
metode ini tidak virtual.Metode virtual binding penjilidan dinamis.
Menyatakan
value
sebagaivirtual
pada tipe yang dikenal secara statisExpression
memastikan bahwa setiap panggilan akan memeriksa apa jenis objek yang sebenarnya, dan memanggil implementasi yang relevanvalue
untuk tipe dinamis itu :Di sini hasilnya
6.86
seperti yang seharusnya, karena metode virtual disebut secara virtual . Ini juga disebut pengikatan dinamis dari panggilan. Pemeriksaan kecil dilakukan, menemukan tipe dinamis objek yang sebenarnya, dan implementasi metode yang relevan untuk tipe dinamis itu, disebut.Implementasi yang relevan adalah yang ada di kelas yang paling spesifik (paling diturunkan).
Perhatikan bahwa implementasi metode dalam kelas turunan di sini tidak ditandai
virtual
, melainkan ditandaioverride
. Mereka dapat ditandaivirtual
tetapi secara otomatis virtual. Ituoverride
Memastikan kata kunci bahwa jika ada tidak seperti metode virtual di beberapa kelas dasar, maka Anda akan mendapatkan error (yang diinginkan).Keburukan melakukan ini tanpa metode virtual
Tanpa
virtual
harus menerapkan beberapa versi Do It Yourself dari ikatan dinamis. Inilah yang umumnya melibatkan downcasting manual yang tidak aman, kompleksitas dan verbositas.Untuk kasus fungsi tunggal, seperti di sini, sudah cukup untuk menyimpan pointer fungsi di objek dan memanggil melalui pointer fungsi, tetapi meskipun demikian itu melibatkan beberapa downcasts yang tidak aman, kompleksitas dan verbosity, untuk dikata:
Salah satu cara positif untuk melihat ini adalah, jika Anda menemukan downcasting yang tidak aman, kompleksitas dan verbositas seperti di atas, maka seringkali metode virtual atau metode dapat sangat membantu.
sumber
Fungsi Virtual digunakan untuk mendukung Polimorfisme Runtime .
Artinya, kata kunci virtual memberi tahu kompiler untuk tidak membuat keputusan (mengikat fungsi) pada waktu kompilasi, agak menunda untuk runtime " .
Anda dapat membuat fungsi virtual dengan mendahului kata kunci
virtual
dalam deklarasi kelas dasar. Sebagai contoh,Ketika Kelas Basis memiliki fungsi anggota virtual, setiap kelas yang mewarisi dari Kelas Basis dapat mendefinisikan kembali fungsi dengan prototipe yang sama persis yaitu fungsi hanya dapat didefinisikan ulang, bukan antarmuka fungsi.
Pointer kelas basis dapat digunakan untuk menunjuk ke objek kelas dasar serta objek kelas turunan.
sumber
Jika kelas dasar adalah
Base
, dan kelas turunannyaDer
, Anda dapat memilikiBase *p
pointer yang menunjuk ke instance dariDer
. Ketika Anda memanggilp->foo();
, jikafoo
ini tidak virtual, makaBase
's versi dijalankan, mengabaikan fakta bahwap
sebenarnya menunjuk ke sebuahDer
. Jika foo adalah virtual,p->foo()
mengeksekusi "leafmost" override darifoo
, sepenuhnya memperhitungkan kelas yang sebenarnya dari item runcing-to. Jadi perbedaan antara virtual dan non-virtual sebenarnya cukup penting: yang pertama memungkinkan polimorfisme runtime , konsep inti dari pemrograman OO, sedangkan yang terakhir tidak.sumber
Need for Virtual Function menjelaskan [Mudah dimengerti]
Output akan menjadi:
Tetapi dengan fungsi virtual:
Output akan menjadi:
Oleh karena itu dengan fungsi virtual Anda dapat mencapai polimorfisme runtime.
sumber
Saya ingin menambahkan lagi penggunaan fungsi Virtual meskipun ia menggunakan konsep yang sama dengan jawaban yang dinyatakan di atas, tetapi saya rasa itu layak disebutkan.
DESTRUKTOR VIRTUAL
Pertimbangkan program ini di bawah, tanpa mendeklarasikan destruktor kelas basis sebagai virtual; memori untuk Cat mungkin tidak dibersihkan.
Keluaran:
Keluaran:
sumber
without declaring Base class destructor as virtual; memory for Cat may not be cleaned up.
Lebih buruk dari itu. Menghapus objek yang diturunkan melalui basis pointer / referensi adalah perilaku murni yang tidak terdefinisi. Jadi, bukan hanya sebagian memori yang bocor. Sebaliknya, program ini sakit-terbentuk, sehingga compiler dapat mengubahnya menjadi apa saja: kode mesin yang terjadi untuk bekerja dengan baik, atau tidak apa-apa, atau panggilan setan dari hidung Anda, atau dll Itu sebabnya, jika program didesain sedemikian cara beberapa pengguna dapat menghapus turunan turunan melalui basis referensi, basis tersebut harus memiliki destruktor virtualAnda harus membedakan antara overriding dan overloading. Tanpa
virtual
kata kunci, Anda hanya membebani metode kelas dasar. Ini tidak lain berarti bersembunyi. Katakanlah Anda memiliki kelas dasarBase
dan kelas turunanSpecialized
yang keduanya mengimplementasikanvoid foo()
. Sekarang Anda memiliki pointer untukBase
menunjuk ke instanceSpecialized
. Ketika Anda memanggilnyafoo()
, Anda dapat mengamati perbedaan yangvirtual
membuat: Jika metode ini virtual, implementasiSpecialized
akan digunakan, jika tidak ada, versi dariBase
akan dipilih. Ini adalah praktik terbaik untuk tidak pernah membebani metode dari kelas dasar. Membuat metode non-virtual adalah cara penulisnya memberi tahu Anda bahwa ekstensi dalam subkelas tidak dimaksudkan.sumber
virtual
Anda, Anda tidak kelebihan beban. Kamu membayangi . Jika kelas dasarB
memiliki satu fungsi atau lebihfoo
, dan kelas turunanD
mendefinisikanfoo
nama, yangfoo
menyembunyikan semuafoo
-s di dalamnyaB
. Mereka tercapai karenaB::foo
menggunakan resolusi lingkup. Untuk mempromosikanB::foo
fungsi menjadiD
kelebihan beban, Anda harus menggunakanusing B::foo
.Jawaban cepat:
Dalam Bjarne Stroustrup, C ++ Programming: Principles and Practice, (14.3):
1.Penggunaan pewarisan, run-time polymorphism, dan enkapsulasi adalah definisi paling umum dari pemrograman berorientasi objek .
2. Anda tidak dapat membuat kode fungsi menjadi lebih cepat atau menggunakan lebih sedikit memori menggunakan fitur bahasa lain untuk memilih di antara alternatif pada waktu berjalan. Bjarne Stroustrup C ++ Pemrograman: Prinsip dan Praktek. (14.3.1) .
3. Sesuatu untuk mengetahui fungsi mana yang benar-benar dipanggil ketika kita memanggil kelas dasar yang berisi fungsi virtual.
sumber
Saya sudah jawab saya dalam bentuk percakapan untuk menjadi lebih baik membaca:
Mengapa kita membutuhkan fungsi virtual?
Karena Polimorfisme.
Apa itu Polimorfisme?
Fakta bahwa pointer basis juga dapat menunjuk ke objek tipe turunan.
Bagaimana definisi Polimorfisme mengarah pada kebutuhan akan fungsi virtual?
Nah, melalui penjilidan awal .
Apa yang mengikat awal?
Pengikatan awal (pengikatan waktu kompilasi) dalam C ++ berarti pemanggilan fungsi diperbaiki sebelum program dijalankan.
Begitu...?
Jadi jika Anda menggunakan tipe dasar sebagai parameter fungsi, kompiler hanya akan mengenali antarmuka dasar, dan jika Anda memanggil fungsi itu dengan argumen apa pun dari kelas turunan, itu akan dipotong, yang bukan apa yang Anda inginkan terjadi.
Jika bukan itu yang kita inginkan terjadi, mengapa ini dibolehkan?
Karena kita membutuhkan Polimorfisme!
Apa manfaat Polimorfisme?
Anda dapat menggunakan pointer tipe dasar sebagai parameter dari fungsi tunggal, dan kemudian pada saat run-time dari program Anda, Anda dapat mengakses setiap antarmuka tipe turunan (mis. Fungsi anggota mereka) tanpa masalah, menggunakan dereferencing dari single itu pointer dasar.
Saya masih tidak tahu apa fungsi virtual yang bagus untuk ...! Dan ini pertanyaan pertamaku!
baik, ini karena Anda terlalu cepat bertanya!
Mengapa kita membutuhkan fungsi virtual?
Asumsikan bahwa Anda memanggil fungsi dengan basis pointer, yang memiliki alamat objek dari salah satu kelas turunannya. Seperti yang telah kita bahas di atas, dalam run-time, pointer ini mengalami dereferensi, sejauh ini bagus, namun, kami mengharapkan metode (== fungsi anggota) "dari kelas turunan kami" akan dieksekusi! Namun, metode yang sama (yang memiliki header yang sama) sudah didefinisikan di kelas dasar, jadi mengapa program Anda harus repot-repot memilih metode lain? Dengan kata lain yang saya maksud, bagaimana Anda bisa membedakan skenario ini dari apa yang biasanya kita lihat terjadi sebelumnya?
Jawaban singkatnya adalah "fungsi anggota Virtual di basis", dan jawaban yang sedikit lebih lama adalah bahwa, "pada langkah ini, jika program melihat fungsi virtual di kelas dasar, ia tahu (menyadari) bahwa Anda sedang mencoba untuk menggunakan polimorfisme "dan seterusnya pergi ke kelas turunan (menggunakan v-table , suatu bentuk pengikatan akhir) untuk menemukan metode lain dengan header yang sama, tetapi dengan - tanpa diduga - implementasi yang berbeda.
Mengapa implementasi berbeda?
Anda kepala menyerah! Baca buku yang bagus !
OK, tunggu tunggu tunggu, mengapa orang repot-repot menggunakan pointer basis, ketika dia hanya bisa menggunakan pointer jenis turunan? Anda menjadi hakim, apakah semua sakit kepala ini sepadan? Lihatlah dua cuplikan ini:
// 1:
// 2:
OK, walaupun saya pikir 1 masih lebih baik dari 2 , Anda bisa menulis 1 seperti ini juga:
// 1:
dan terlebih lagi, Anda harus menyadari bahwa ini belum menggunakan semua hal yang telah saya jelaskan kepada Anda sejauh ini. Alih-alih ini, anggap sebagai contoh situasi di mana Anda memiliki fungsi dalam program Anda yang menggunakan metode dari masing-masing kelas turunan masing-masing (getMonthBenefit ()):
Sekarang, coba tulis ulang ini, tanpa sakit kepala!
Dan sebenarnya, ini mungkin juga contoh yang dibuat-buat!
sumber
Ketika Anda memiliki fungsi di kelas dasar, Anda bisa
Redefine
atauOverride
itu di kelas turunan.Mendefinisikan ulang metode : Implementasi baru untuk metode kelas dasar diberikan dalam kelas turunan. Tidak memfasilitasi
Dynamic binding
.Mengganti metode :
Redefining
avirtual method
dari kelas dasar di kelas turunan. Metode virtual memfasilitasi Pengikatan Dinamis .Jadi, ketika Anda berkata:
Anda tidak menimpanya karena metode di kelas dasar bukan virtual, melainkan Anda mendefinisikannya kembali
sumber
Ini membantu jika Anda mengetahui mekanisme yang mendasarinya. C ++ memformalkan beberapa teknik pengkodean yang digunakan oleh programmer C, "kelas" diganti dengan menggunakan "overlay" - struct dengan bagian header umum akan digunakan untuk menangani objek dari berbagai jenis tetapi dengan beberapa data umum atau operasi. Biasanya struct dasar overlay (bagian umum) memiliki pointer ke tabel fungsi yang menunjuk ke set rutin yang berbeda untuk setiap jenis objek. C ++ melakukan hal yang sama tetapi menyembunyikan mekanisme yaitu C ++ di
ptr->func(...)
mana func adalah virtual seperti C(*ptr->func_table[func_num])(ptr,...)
, di mana perubahan antara kelas turunan adalah konten func_table. [Metode ptr-> func () non-virtual hanya diterjemahkan ke mangled_func (ptr, ..).]Hasilnya adalah bahwa Anda hanya perlu memahami kelas dasar untuk memanggil metode kelas turunan, yaitu jika suatu rutin memahami kelas A, Anda dapat meneruskannya dengan kelas B penunjuk maka metode virtual yang dipanggil akan menjadi B daripada A karena Anda pergi melalui tabel fungsi B menunjuk pada.
sumber
Kata kunci virtual memberi tahu kompiler bahwa ia seharusnya tidak melakukan pengikatan awal. Sebagai gantinya, itu harus secara otomatis menginstal semua mekanisme yang diperlukan untuk melakukan ikatan yang terlambat. Untuk mencapai hal ini, compiler1 khas membuat tabel tunggal (disebut VTABLE) untuk setiap kelas yang berisi fungsi virtual. Kompiler menempatkan alamat fungsi virtual untuk kelas tertentu di VTABLE. Di setiap kelas dengan fungsi virtual, itu diam-diam menempatkan pointer, yang disebut vpointer (disingkat VPTR), yang menunjuk ke VTABLE untuk objek itu. Ketika Anda membuat panggilan fungsi virtual melalui pointer kelas dasar, kompiler diam-diam memasukkan kode untuk mengambil VPTR dan mencari alamat fungsi di VTABLE, sehingga memanggil fungsi yang benar dan menyebabkan keterlambatan terjadi.
Lebih detail dalam tautan ini http://cplusplusinterviews.blogspot.sg/2015/04/virtual-mechanism.html
sumber
Kata kunci virtual memaksa kompiler untuk memilih implementasi metode yang didefinisikan dalam kelas objek daripada di kelas pointer .
Dalam contoh di atas, Shape :: getName akan dipanggil secara default, kecuali getName () didefinisikan sebagai virtual dalam bentuk kelas Base. Ini memaksa kompiler untuk mencari implementasi getName () di kelas Triangle daripada di kelas Shape.
The tabel virtual adalah mekanisme di mana compiler terus melacak dari berbagai implementasi-metode virtual dari subclass. Ini juga disebut dispatch dinamis, dan ada yang beberapa overhead yang terkait dengan itu.
Akhirnya, mengapa virtual bahkan diperlukan di C ++, mengapa tidak menjadikannya perilaku default seperti di Jawa?
sumber
Mengapa kita membutuhkan fungsi virtual?
Fungsi virtual menghindari masalah typecasting yang tidak perlu, dan beberapa dari kita dapat berdebat bahwa mengapa kita memerlukan fungsi virtual ketika kita dapat menggunakan pointer kelas turunan untuk memanggil fungsi spesifik di kelas turunan! Jawabannya adalah - itu membatalkan seluruh gagasan warisan dalam sistem besar pengembangan, di mana memiliki objek kelas dasar pointer tunggal sangat diinginkan.
Mari kita bandingkan dua program sederhana di bawah ini untuk memahami pentingnya fungsi virtual:
Program tanpa fungsi virtual:
KELUARAN:
Program dengan fungsi virtual:
KELUARAN:
Dengan menganalisis kedua output secara cermat, orang dapat memahami pentingnya fungsi virtual.
sumber
OOP Jawab: Subtipe Polimorfisme
Dalam C ++, metode virtual diperlukan untuk mewujudkan polimorfisme , lebih tepatnya subtipe atau polimorfisme subtipe jika Anda menerapkan definisi dari wikipedia.
Wikipedia, Subtyping, 2019-01-09: Dalam teori bahasa pemrograman, subtyping (juga subtipe polimorfisme atau polimorfisme inklusi) adalah bentuk polimorfisme tipe di mana subtipe adalah tipe data yang terkait dengan tipe data lain (supertipe) oleh beberapa gagasan substitusi, artinya elemen program, biasanya subrutin atau fungsi, ditulis untuk beroperasi pada elemen supertipe juga dapat beroperasi pada elemen subtipe.
CATATAN: Subtipe berarti kelas dasar, dan subtipe berarti kelas bawaan.
Bacaan lebih lanjut tentang Subtipe Polimorfisme
Jawaban Teknis: Pengiriman Dinamis
Jika Anda memiliki pointer ke kelas dasar, maka panggilan metode (yang dinyatakan sebagai virtual) akan dikirim ke metode kelas aktual objek yang dibuat. Ini adalah bagaimana Subtipe Polimorfisme direalisasikan adalah C ++.
Bacaan lebih lanjut Polimorfisme dalam C ++ dan Dynamic Dispatch
Jawaban Implementasi: Membuat entri vtable
Untuk setiap pengubah "virtual" pada metode, kompiler C ++ biasanya membuat entri dalam vtable kelas di mana metode tersebut dideklarasikan. Ini adalah bagaimana kompiler C ++ umum mewujudkan Dynamic Dispatch .
Bacaan lanjutan vtables
Kode Contoh
Output dari Kode Contoh
Diagram kelas kode contoh UML
sumber
Berikut ini adalah contoh lengkap yang menggambarkan mengapa metode virtual digunakan.
sumber
Tentang efisiensi, fungsi virtual sedikit kurang efisien sebagai fungsi pengikatan awal.
"Mekanisme panggilan virtual ini dapat dibuat hampir seefisien mekanisme" panggilan fungsi normal "(dalam 25%). Overhead ruangnya adalah satu penunjuk di setiap objek kelas dengan fungsi virtual plus satu vtbl untuk setiap kelas tersebut" [ A tour C ++ oleh Bjarne Stroustrup]
sumber
if(param1>param2) return cst;
mana kompiler dapat mengurangi seluruh panggilan fungsi ke konstanta dalam beberapa kasus).Metode virtual digunakan dalam desain antarmuka. Misalnya di Windows ada antarmuka bernama IUnknown seperti di bawah ini:
Metode-metode ini diserahkan kepada pengguna antarmuka untuk diimplementasikan. Mereka sangat penting untuk penciptaan dan penghancuran objek tertentu yang harus mewarisi IUnknown. Dalam hal ini run-time mengetahui tiga metode dan mengharapkannya untuk diimplementasikan ketika memanggilnya. Jadi dalam arti mereka bertindak sebagai kontrak antara objek itu sendiri dan apa pun yang menggunakan objek itu.
sumber
the run-time is aware of the three methods and expects them to be implemented
Karena mereka adalah virtual murni, tidak ada cara untuk membuat instanceIUnknown
, dan semua subclass harus mengimplementasikan semua metode tersebut untuk hanya mengkompilasi. Tidak ada bahaya jika tidak mengimplementasikannya dan hanya menemukan itu pada saat runtime (tapi tentu saja orang bisa salah mengimplementasikannya, tentu saja!). Dan wow, hari ini saya belajar Windows#define
sa macro dengan kata ituinterface
, mungkin karena pengguna mereka tidak bisa hanya (A) melihat awalanI
dalam nama atau (B) melihat kelas untuk melihat itu sebuah antarmuka. Ughsaya pikir Anda merujuk pada fakta setelah suatu metode dinyatakan virtual Anda tidak perlu menggunakan kata kunci 'virtual' dalam menimpa.
Jika Anda tidak menggunakan 'virtual' dalam deklarasi foo Pangkalan maka foo Derived akan membayangi saja.
sumber
Berikut ini adalah versi gabungan dari kode C ++ untuk dua jawaban pertama.
Dua hasil berbeda adalah:
Tanpa #define virtual , ia mengikat pada waktu kompilasi. Animal * iklan dan func (Hewan *) semua mengarah ke metode mengatakan () Hewan.
Dengan #define virtual , ia mengikat saat dijalankan. Dog * d, Animal * iklan dan func (Animal *) menunjuk / merujuk pada metode Dog's said () karena Dog adalah tipe objek mereka. Kecuali jika [Dog's said () "woof"] metode tidak didefinisikan, itu akan menjadi yang pertama dicari di pohon kelas, yaitu kelas turunan dapat menimpa metode kelas dasar mereka [Animal's said ()].
Sangat menarik untuk dicatat bahwa semua atribut kelas (data dan metode) dalam Python efektif secara virtual . Karena semua objek dibuat secara dinamis saat runtime, tidak ada deklarasi tipe atau kebutuhan untuk kata kunci virtual. Di bawah ini adalah versi kode Python:
Outputnya adalah:
yang identik dengan mendefinisikan virtual C ++. Perhatikan bahwa d dan iklan adalah dua variabel penunjuk berbeda yang merujuk / menunjuk ke turunan Dog yang sama. Ekspresi (iklan adalah d) mengembalikan True dan nilainya adalah < objek .dog utama yang sama di 0xb79f72cc>
sumber
Apakah Anda terbiasa dengan pointer fungsi? Fungsi virtual adalah ide yang serupa, kecuali Anda dapat dengan mudah mengikat data ke fungsi virtual (sebagai anggota kelas). Tidak mudah untuk mengikat data ke pointer fungsi. Bagi saya, ini adalah perbedaan konsep utama. Banyak jawaban lain di sini hanya mengatakan "karena ... polimorfisme!"
sumber
Kami membutuhkan metode virtual untuk mendukung "Jalankan Polimorfisme Waktu". Saat Anda merujuk ke objek kelas turunan menggunakan pointer atau referensi ke kelas dasar, Anda bisa memanggil fungsi virtual untuk objek itu dan menjalankan versi fungsi kelas turunan.
sumber
Intinya adalah bahwa fungsi virtual membuat hidup lebih mudah. Mari kita gunakan beberapa ide M Perry dan menjelaskan apa yang akan terjadi jika kita tidak memiliki fungsi virtual dan sebagai gantinya hanya dapat menggunakan pointer fungsi anggota. Kami miliki, dalam estimasi normal tanpa fungsi virtual:
Ok, jadi itu yang kita tahu. Sekarang mari kita coba melakukannya dengan pointer fungsi anggota:
Meskipun kita dapat melakukan beberapa hal dengan pointer fungsi anggota, mereka tidak sefleksibel fungsi virtual. Sulit untuk menggunakan pointer fungsi anggota di kelas; pointer fungsi anggota hampir, paling tidak dalam praktik saya, selalu harus dipanggil di fungsi utama atau dari dalam fungsi anggota seperti dalam contoh di atas.
Di sisi lain, fungsi virtual, sementara mereka mungkin memiliki beberapa fungsi-pointer overhead, melakukan penyederhanaan hal-hal secara dramatis.
EDIT: Ada metode lain yang mirip dengan eddietree: c ++ fungsi virtual vs fungsi penunjuk anggota (perbandingan kinerja) .
sumber