Saya ingin tahu apa itu " kelas dasar virtual " dan apa artinya.
Izinkan saya menunjukkan sebuah contoh:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
Saya ingin tahu apa itu " kelas dasar virtual " dan apa artinya.
Izinkan saya menunjukkan sebuah contoh:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
Jawaban:
Kelas dasar virtual, yang digunakan dalam warisan virtual, adalah cara untuk mencegah beberapa "instance" dari kelas yang diberikan muncul dalam hierarki warisan ketika menggunakan banyak pewarisan.
Pertimbangkan skenario berikut:
class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};
Hirarki kelas di atas menghasilkan "berlian menakutkan" yang terlihat seperti ini:
A
/ \
B C
\ /
D
Sebuah instance dari D akan terdiri dari B, yang termasuk A, dan C yang juga termasuk A. Jadi Anda memiliki dua "instance" (karena ingin ekspresi yang lebih baik) dari A.
Ketika Anda memiliki skenario ini, Anda memiliki kemungkinan ambiguitas. Apa yang terjadi ketika Anda melakukan ini:
D d;
d.Foo(); // is this B's Foo() or C's Foo() ??
Warisan virtual ada untuk menyelesaikan masalah ini. Saat Anda menentukan virtual saat mewarisi kelas Anda, Anda memberi tahu kompiler bahwa Anda hanya menginginkan satu instance.
class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
Ini berarti bahwa hanya ada satu "instance" dari A yang termasuk dalam hierarki. Karenanya
D d;
d.Foo(); // no longer ambiguous
Ini adalah ringkasan mini. Untuk informasi lebih lanjut, baca ini dan ini . Contoh yang baik juga tersedia di sini .
virtual
, maka tata letak objek terlihat seperti berlian; dan jika kita tidak menggunakan virtual
maka tata letak objek terlihat seperti struktur pohon yang berisi dua A
s
Sebagai catatan, masalah dengan Dreaded Diamond adalah bahwa kelas dasar hadir beberapa kali. Jadi dengan warisan reguler, Anda yakin telah:
A
/ \
B C
\ /
D
Namun dalam tata letak memori, Anda memiliki:
A A
| |
B C
\ /
D
Ini menjelaskan mengapa saat menelepon D::foo()
, Anda memiliki masalah ambiguitas. Tetapi masalah sebenarnya muncul ketika Anda ingin menggunakan variabel anggota A
. Sebagai contoh, katakanlah kita memiliki:
class A
{
public :
foo() ;
int m_iValue ;
} ;
Ketika Anda akan mencoba mengakses m_iValue
dari D
, kompiler akan memprotes, karena dalam hierarki, ia akan melihat dua m_iValue
, bukan satu. Dan jika Anda memodifikasi satu, katakanlah, B::m_iValue
(yang merupakan A::m_iValue
induk dari B
), C::m_iValue
tidak akan dimodifikasi (itu adalah A::m_iValue
induk dariC
).
Di sinilah warisan virtual berguna, karena dengan itu, Anda akan kembali ke tata letak berlian yang sebenarnya, dengan tidak hanya satu foo()
metode saja, tetapi juga satu dan hanya satu m_iValue
.
Membayangkan:
A
memiliki beberapa fitur dasar.B
menambah semacam array data yang keren (misalnya)C
menambah beberapa fitur keren seperti pola pengamat (misalnya, aktif m_iValue
).D
mewarisi dari B
dan C
, dan dengan demikian dari A
.Dengan warisan normal, pengubahan m_iValue
dari D
adalah ambigu dan ini harus diselesaikan. Bahkan jika ada, ada dua m_iValues
di dalamD
, jadi Anda sebaiknya mengingatnya dan memperbarui keduanya sekaligus.
Dengan warisan virtual, memodifikasi m_iValue
dari D
adalah ok ... Tapi ... Katakanlah Anda miliki D
. Melalui C
antarmuka, Anda memasang pengamat. Dan melalui B
antarmuka, Anda memperbarui array keren, yang memiliki efek samping mengubah langsung m_iValue
...
Karena perubahan m_iValue
dilakukan secara langsung (tanpa menggunakan metode pengakses virtual), pengamat "mendengarkan" melalui C
tidak akan dipanggil, karena kode yang menerapkan mendengarkan dalam C
, dan B
tidak tahu tentang itu ...
Jika Anda memiliki berlian dalam hierarki Anda, itu berarti Anda memiliki kemungkinan 95% untuk melakukan sesuatu yang salah dengan hierarki tersebut.
Menjelaskan multiple-inheritance dengan basis virtual membutuhkan pengetahuan tentang model objek C ++. Dan menjelaskan topiknya dengan jelas paling baik dilakukan dalam sebuah artikel dan bukan dalam kotak komentar.
Penjelasan terbaik dan dapat dibaca yang saya temukan yang menyelesaikan semua keraguan saya pada subjek ini adalah artikel ini: http://www.phpcompiler.org/articles/virtualinheritance.html
Anda benar-benar tidak perlu membaca apa pun tentang topik (kecuali jika Anda adalah penulis kompiler) setelah membaca itu ...
Kelas dasar virtual adalah kelas yang tidak bisa dipakai: Anda tidak bisa membuat objek langsung dari itu.
Saya pikir Anda membingungkan dua hal yang sangat berbeda. Warisan virtual tidak sama dengan kelas abstrak. Warisan virtual mengubah perilaku panggilan fungsi; kadang-kadang ia menyelesaikan panggilan fungsi yang sebaliknya akan ambigu, kadang-kadang ia menolak penanganan panggilan fungsi ke kelas selain yang diharapkan dalam warisan non-virtual.
Saya ingin menambahkan klarifikasi jenis OJ.
Warisan virtual tidak datang tanpa harga. Seperti halnya semua hal virtual, Anda mendapatkan kinerja yang baik. Ada cara mengatasi hit kinerja ini yang mungkin kurang elegan.
Alih-alih memecahkan berlian dengan menurunkan secara virtual, Anda dapat menambahkan lapisan lain ke berlian, untuk mendapatkan sesuatu seperti ini:
B
/ \
D11 D12
| |
D21 D22
\ /
DD
Tidak ada kelas yang mewarisi secara virtual, semua mewarisi secara publik. Kelas D21 dan D22 kemudian akan menyembunyikan fungsi virtual f () yang ambigu untuk DD, mungkin dengan mendeklarasikan fungsi privat. Mereka masing-masing mendefinisikan fungsi wrapper, masing-masing f1 () dan f2 (), masing-masing memanggil kelas-lokal (pribadi) f (), sehingga menyelesaikan konflik. Panggilan kelas DD f1 () jika ingin D11 :: f () dan f2 () jika ingin D12 :: f (). Jika Anda mendefinisikan pembungkus inline Anda mungkin akan mendapatkan sekitar nol overhead.
Tentu saja, jika Anda dapat mengubah D11 dan D12 maka Anda dapat melakukan trik yang sama di dalam kelas-kelas ini, tetapi seringkali itu tidak terjadi.
Selain apa yang telah dikatakan tentang multiple and virtual inheritance (s), ada artikel yang sangat menarik di Dr Dobb's Journal: Multiple Warisan Dianggap Berguna
Anda sedikit membingungkan. Saya tidak tahu jika Anda mencampur beberapa konsep.
Anda tidak memiliki kelas dasar virtual di OP Anda. Anda hanya memiliki kelas dasar.
Anda melakukan warisan virtual. Ini biasanya digunakan dalam multiple inheritance sehingga beberapa kelas turunan menggunakan anggota kelas dasar tanpa mereproduksi mereka.
Kelas dasar dengan fungsi virtual murni tidak dapat dipakai. ini membutuhkan sintaksis yang dimiliki Paulus. Ini biasanya digunakan sehingga kelas turunan harus mendefinisikan fungsi-fungsi tersebut.
Saya tidak ingin menjelaskan lagi tentang ini karena saya tidak sepenuhnya mendapatkan apa yang Anda minta.
Ini berarti panggilan ke fungsi virtual akan diteruskan ke kelas "benar".
C ++ FAQ Lite FTW.
Singkatnya, ini sering digunakan dalam skenario multiple-inheritance, di mana hierarki "berlian" terbentuk. Warisan virtual kemudian akan memecah ambiguitas yang dibuat di kelas bawah, ketika Anda memanggil fungsi di kelas itu dan fungsi harus diselesaikan ke kelas D1 atau D2 di atas kelas bawah itu. Lihat item FAQ untuk diagram dan detailnya.
Ini juga digunakan dalam delegasi saudari , fitur yang kuat (meskipun tidak untuk yang lemah hati). Lihat ini FAQ .
Juga lihat Butir 40 dalam Efektif C ++ edisi ke-3 (43 dalam edisi ke-2).
Contoh penggunaan runnable warisan berlian
Contoh ini menunjukkan bagaimana menggunakan kelas dasar virtual dalam skenario tipikal: untuk menyelesaikan warisan berlian.
#include <cassert>
class A {
public:
A(){}
A(int i) : i(i) {}
int i;
virtual int f() = 0;
virtual int g() = 0;
virtual int h() = 0;
};
class B : public virtual A {
public:
B(int j) : j(j) {}
int j;
virtual int f() { return this->i + this->j; }
};
class C : public virtual A {
public:
C(int k) : k(k) {}
int k;
virtual int g() { return this->i + this->k; }
};
class D : public B, public C {
public:
D(int i, int j, int k) : A(i), B(j), C(k) {}
virtual int h() { return this->i + this->j + this->k; }
};
int main() {
D d = D(1, 2, 4);
assert(d.f() == 3);
assert(d.g() == 5);
assert(d.h() == 7);
}
assert(A::aDefault == 0);
dari fungsi utama memberi saya kesalahan kompilasi: aDefault is not a member of A
menggunakan gcc 5.4.0. Apa yang harus dilakukan?
Kelas virtual tidak sama dengan warisan virtual. Kelas virtual yang tidak dapat Anda instantiate, virtual inheritance adalah sesuatu yang sepenuhnya berbeda.
Wikipedia menggambarkannya lebih baik daripada yang saya bisa. http://en.wikipedia.org/wiki/Virtual_inheritance
Dengan 3 level non-diamond non-virtual-inheritance inheritance, ketika Anda membuat instance objek paling baru, baru dipanggil dan ukuran yang diperlukan untuk objek diselesaikan dari tipe kelas oleh kompiler dan diteruskan ke yang baru.
baru memiliki tanda tangan:
_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
Dan membuat panggilan ke malloc
, mengembalikan pointer kosong
Ini kemudian diteruskan ke konstruktor dari objek yang paling diturunkan, yang akan segera memanggil konstruktor tengah dan kemudian konstruktor tengah akan segera memanggil konstruktor dasar. Basis kemudian menyimpan pointer ke tabel virtualnya di awal objek dan kemudian atributnya setelah itu. Ini kemudian kembali ke konstruktor tengah yang akan menyimpan pointer tabel virtualnya di lokasi yang sama dan kemudian atributnya setelah atribut yang akan disimpan oleh konstruktor dasar. Ini mengembalikan ke konstruktor yang paling diturunkan, yang menyimpan pointer ke tabel virtualnya di lokasi yang sama dan kemudian atributnya setelah atribut yang akan disimpan oleh konstruktor tengah.
Karena pointer tabel virtual ditimpa, pointer tabel virtual akhirnya selalu menjadi salah satu kelas yang paling diturunkan. Virtualness merambat ke kelas yang paling diturunkan jadi jika suatu fungsi virtual di kelas menengah, itu akan menjadi virtual di kelas yang paling diturunkan tetapi bukan kelas dasar. Jika Anda secara polimorfik membuat instance dari kelas yang paling diturunkan ke pointer ke kelas dasar maka kompiler tidak akan menyelesaikan ini untuk panggilan tidak langsung ke tabel virtual dan sebagai gantinya akan memanggil fungsi secara langsung A::function()
. Jika suatu fungsi adalah virtual untuk tipe yang Anda gunakan, maka ia akan memutuskan panggilan ke tabel virtual yang akan selalu menjadi kelas yang paling diturunkan. Jika ini bukan virtual untuk jenis itu maka itu hanya akan memanggilType::function()
dan meneruskan pointer objek ke sana, cor ke Type.
Sebenarnya ketika saya mengatakan pointer ke tabel virtualnya, itu sebenarnya selalu merupakan offset 16 ke dalam tabel virtual.
vtable for Base:
.quad 0
.quad typeinfo for Base
.quad Base::CommonFunction()
.quad Base::VirtualFunction()
pointer is typically to the first function i.e.
mov edx, OFFSET FLAT:vtable for Base+16
virtual
tidak diperlukan lagi di kelas yang lebih diturunkan jika itu virtual di kelas yang lebih rendah karena menyebar. Tetapi dapat digunakan untuk menunjukkan bahwa fungsi tersebut memang merupakan fungsi virtual, tanpa harus memeriksa kelas-kelas yang diwarisi tipe definisi.
override
adalah penjaga kompiler lain yang mengatakan bahwa fungsi ini mengesampingkan sesuatu dan jika tidak maka melemparkan kesalahan kompilator.
= 0
berarti bahwa ini adalah fungsi abstrak
final
mencegah fungsi virtual diimplementasikan lagi di kelas yang lebih diturunkan dan akan memastikan bahwa tabel virtual dari kelas yang paling diturunkan berisi fungsi akhir dari kelas itu.
= default
membuatnya eksplisit dalam dokumentasi bahwa kompiler akan menggunakan implementasi default
= delete
berikan kesalahan penyusun jika panggilan ke ini dilakukan
Mempertimbangkan
class Base
{
int a = 1;
int b = 2;
public:
void virtual CommonFunction(){} ;
void virtual VirtualFunction(){} ;
};
class DerivedClass1: virtual public Base
{
int c = 3;
public:
void virtual DerivedCommonFunction(){} ;
void virtual VirtualFunction(){} ;
};
class DerivedClass2 : virtual public Base
{
int d = 4;
public:
//void virtual DerivedCommonFunction(){} ;
void virtual VirtualFunction(){} ;
void virtual DerivedCommonFunction2(){} ;
};
class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
int e = 5;
public:
void virtual DerivedDerivedCommonFunction(){} ;
void virtual VirtualFunction(){} ;
};
int main () {
DerivedDerivedClass* d = new DerivedDerivedClass;
d->VirtualFunction();
d->DerivedCommonFunction();
d->DerivedCommonFunction2();
d->DerivedDerivedCommonFunction();
((DerivedClass2*)d)->DerivedCommonFunction2();
((Base*)d)->VirtualFunction();
}
Tanpa mewarisi kelas bass Anda akan mendapatkan objek yang terlihat seperti ini:
Alih-alih ini:
Yaitu akan ada 2 objek dasar.
Dalam situasi warisan berlian virtual di atas, setelah baru dipanggil, ia memanggil konstruktor yang paling diturunkan dan dalam konstruktor itu, ia memanggil semua 3 konstruktor turunan yang melewati offset ke dalam tabel tabel virtualnya, alih-alih memanggil hanya memanggil DerivedClass1::DerivedClass1()
danDerivedClass2::DerivedClass2()
kemudian mereka berdua callingBase::Base()
Berikut ini semua dikompilasi dalam mode debug -O0 sehingga akan ada perakitan yang berlebihan
main:
.LFB8:
push rbp
mov rbp, rsp
push rbx
sub rsp, 24
mov edi, 48 //pass size to new
call operator new(unsigned long) //call new
mov rbx, rax //move the address of the allocation to rbx
mov rdi, rbx //move it to rdi i.e. pass to the call
call DerivedDerivedClass::DerivedDerivedClass() [complete object constructor] //construct on this address
mov QWORD PTR [rbp-24], rbx //store the address of the object on the stack as d
DerivedDerivedClass::DerivedDerivedClass() [complete object constructor]:
.LFB20:
push rbp
mov rbp, rsp
sub rsp, 16
mov QWORD PTR [rbp-8], rdi
.LBB5:
mov rax, QWORD PTR [rbp-8] // object address now in rax
add rax, 32 //increment address by 32
mov rdi, rax // move object address+32 to rdi i.e. pass to call
call Base::Base() [base object constructor]
mov rax, QWORD PTR [rbp-8] //move object address to rax
mov edx, OFFSET FLAT:VTT for DerivedDerivedClass+8 //move address of VTT+8 to edx
mov rsi, rdx //pass VTT+8 address as 2nd parameter
mov rdi, rax //object address as first
call DerivedClass1::DerivedClass1() [base object constructor]
mov rax, QWORD PTR [rbp-8] //move object address to rax
add rax, 16 //increment object address by 16
mov edx, OFFSET FLAT:VTT for DerivedDerivedClass+24 //store address of VTT+24 in edx
mov rsi, rdx //pass address of VTT+24 as second parameter
mov rdi, rax //address of object as first
call DerivedClass2::DerivedClass2() [base object constructor]
mov edx, OFFSET FLAT:vtable for DerivedDerivedClass+24 //move this to edx
mov rax, QWORD PTR [rbp-8] // object address now in rax
mov QWORD PTR [rax], rdx. //store address of vtable for DerivedDerivedClass+24 at the start of the object
mov rax, QWORD PTR [rbp-8] // object address now in rax
add rax, 32 // increment object address by 32
mov edx, OFFSET FLAT:vtable for DerivedDerivedClass+120 //move this to edx
mov QWORD PTR [rax], rdx //store vtable for DerivedDerivedClass+120 at object+32 (Base)
mov edx, OFFSET FLAT:vtable for DerivedDerivedClass+72 //store this in edx
mov rax, QWORD PTR [rbp-8] //move object address to rax
mov QWORD PTR [rax+16], rdx //store vtable for DerivedDerivedClass+72 at object+16 (DerivedClass2)
mov rax, QWORD PTR [rbp-8]
mov DWORD PTR [rax+28], 5
.LBE5:
nop
leave
ret
Itu panggilan Base::Base()
dengan pointer ke objek offset 32. Base menyimpan pointer ke tabel virtualnya di alamat yang diterimanya dan anggota-anggotanya setelah itu.
Base::Base() [base object constructor]:
.LFB11:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi //stores address of object on stack (-O0)
.LBB2:
mov edx, OFFSET FLAT:vtable for Base+16 //puts vtable for Base+16 in edx
mov rax, QWORD PTR [rbp-8] //copies address of object from stack to rax
mov QWORD PTR [rax], rdx //stores it address of object
mov rax, QWORD PTR [rbp-8] //copies address of object on stack to rax again
mov DWORD PTR [rax+8], 1 //stores a = 1 in the object
mov rax, QWORD PTR [rbp-8] //junk from -O0
mov DWORD PTR [rax+12], 2 //stores b = 2 in the object
.LBE2:
nop
pop rbp
ret
DerivedDerivedClass::DerivedDerivedClass()
kemudian memanggil DerivedClass1::DerivedClass1()
dengan pointer ke objek offset 0 dan juga melewati alamatVTT for DerivedDerivedClass+8
DerivedClass1::DerivedClass1() [base object constructor]:
.LFB14:
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], rdi //address of object
mov QWORD PTR [rbp-16], rsi //address of VTT+8
.LBB3:
mov rax, QWORD PTR [rbp-16] //address of VTT+8 now in rax
mov rdx, QWORD PTR [rax] //address of DerivedClass1-in-DerivedDerivedClass+24 now in rdx
mov rax, QWORD PTR [rbp-8] //address of object now in rax
mov QWORD PTR [rax], rdx //store address of DerivedClass1-in-.. in the object
mov rax, QWORD PTR [rbp-8] // address of object now in rax
mov rax, QWORD PTR [rax] //address of DerivedClass1-in.. now implicitly in rax
sub rax, 24 //address of DerivedClass1-in-DerivedDerivedClass+0 now in rax
mov rax, QWORD PTR [rax] //value of 32 now in rax
mov rdx, rax // now in rdx
mov rax, QWORD PTR [rbp-8] //address of object now in rax
add rdx, rax //address of object+32 now in rdx
mov rax, QWORD PTR [rbp-16] //address of VTT+8 now in rax
mov rax, QWORD PTR [rax+8] //address of DerivedClass1-in-DerivedDerivedClass+72 (Base::CommonFunction()) now in rax
mov QWORD PTR [rdx], rax //store at address object+32 (offset to Base)
mov rax, QWORD PTR [rbp-8] //store address of object in rax, return
mov DWORD PTR [rax+8], 3 //store its attribute c = 3 in the object
.LBE3:
nop
pop rbp
ret
VTT for DerivedDerivedClass:
.quad vtable for DerivedDerivedClass+24
.quad construction vtable for DerivedClass1-in-DerivedDerivedClass+24
.quad construction vtable for DerivedClass1-in-DerivedDerivedClass+72
.quad construction vtable for DerivedClass2-in-DerivedDerivedClass+24
.quad construction vtable for DerivedClass2-in-DerivedDerivedClass+72
.quad vtable for DerivedDerivedClass+120
.quad vtable for DerivedDerivedClass+72
construction vtable for DerivedClass1-in-DerivedDerivedClass:
.quad 32
.quad 0
.quad typeinfo for DerivedClass1
.quad DerivedClass1::DerivedCommonFunction()
.quad DerivedClass1::VirtualFunction()
.quad -32
.quad 0
.quad -32
.quad typeinfo for DerivedClass1
.quad Base::CommonFunction()
.quad virtual thunk to DerivedClass1::VirtualFunction()
construction vtable for DerivedClass2-in-DerivedDerivedClass:
.quad 16
.quad 0
.quad typeinfo for DerivedClass2
.quad DerivedClass2::VirtualFunction()
.quad DerivedClass2::DerivedCommonFunction2()
.quad -16
.quad 0
.quad -16
.quad typeinfo for DerivedClass2
.quad Base::CommonFunction()
.quad virtual thunk to DerivedClass2::VirtualFunction()
vtable for DerivedDerivedClass:
.quad 32
.quad 0
.quad typeinfo for DerivedDerivedClass
.quad DerivedClass1::DerivedCommonFunction()
.quad DerivedDerivedClass::VirtualFunction()
.quad DerivedDerivedClass::DerivedDerivedCommonFunction()
.quad 16
.quad -16
.quad typeinfo for DerivedDerivedClass
.quad non-virtual thunk to DerivedDerivedClass::VirtualFunction()
.quad DerivedClass2::DerivedCommonFunction2()
.quad -32
.quad 0
.quad -32
.quad typeinfo for DerivedDerivedClass
.quad Base::CommonFunction()
.quad virtual thunk to DerivedDerivedClass::VirtualFunction()
virtual thunk to DerivedClass1::VirtualFunction():
mov r10, QWORD PTR [rdi]
add rdi, QWORD PTR [r10-32]
jmp .LTHUNK0
virtual thunk to DerivedClass2::VirtualFunction():
mov r10, QWORD PTR [rdi]
add rdi, QWORD PTR [r10-32]
jmp .LTHUNK1
virtual thunk to DerivedDerivedClass::VirtualFunction():
mov r10, QWORD PTR [rdi]
add rdi, QWORD PTR [r10-32]
jmp .LTHUNK2
non-virtual thunk to DerivedDerivedClass::VirtualFunction():
sub rdi, 16
jmp .LTHUNK3
.set .LTHUNK0,DerivedClass1::VirtualFunction()
.set .LTHUNK1,DerivedClass2::VirtualFunction()
.set .LTHUNK2,DerivedDerivedClass::VirtualFunction()
.set .LTHUNK3,DerivedDerivedClass::VirtualFunction()
DerivedDerivedClass::DerivedDerivedClass()
kemudian melewati alamat dari objek + 16 dan alamat VTT untuk DerivedDerivedClass+24
untuk DerivedClass2::DerivedClass2()
yang perakitan identik dengan DerivedClass1::DerivedClass1()
kecuali untuk garis mov DWORD PTR [rax+8], 3
yang jelas memiliki 4 bukannya 3 untukd = 4
.
Setelah ini, ia mengganti semua 3 pointer tabel virtual dalam objek dengan pointer ke offset di DerivedDerivedClass
vtable ke representasi untuk kelas itu.
d->VirtualFunction();
:
mov rax, QWORD PTR [rbp-24] //store pointer to virtual table in rax
mov rax, QWORD PTR [rax] //dereference and store in rax
add rax, 8 // call the 2nd function in the table
mov rdx, QWORD PTR [rax] //dereference
mov rax, QWORD PTR [rbp-24]
mov rdi, rax
call rdx
d->DerivedCommonFunction();
:
mov rax, QWORD PTR [rbp-24]
mov rdx, QWORD PTR [rbp-24]
mov rdx, QWORD PTR [rdx]
mov rdx, QWORD PTR [rdx]
mov rdi, rax
call rdx
d->DerivedCommonFunction2();
:
mov rax, QWORD PTR [rbp-24]
lea rdx, [rax+16]
mov rax, QWORD PTR [rbp-24]
mov rax, QWORD PTR [rax+16]
add rax, 8
mov rax, QWORD PTR [rax]
mov rdi, rdx
call rax
d->DerivedDerivedCommonFunction();
:
mov rax, QWORD PTR [rbp-24]
mov rax, QWORD PTR [rax]
add rax, 16
mov rdx, QWORD PTR [rax]
mov rax, QWORD PTR [rbp-24]
mov rdi, rax
call rdx
((DerivedClass2*)d)->DerivedCommonFunction2();
:
cmp QWORD PTR [rbp-24], 0
je .L14
mov rax, QWORD PTR [rbp-24]
add rax, 16
jmp .L15
.L14:
mov eax, 0
.L15:
cmp QWORD PTR [rbp-24], 0
cmp QWORD PTR [rbp-24], 0
je .L18
mov rdx, QWORD PTR [rbp-24]
add rdx, 16
jmp .L19
.L18:
mov edx, 0
.L19:
mov rdx, QWORD PTR [rdx]
add rdx, 8
mov rdx, QWORD PTR [rdx]
mov rdi, rax
call rdx
((Base*)d)->VirtualFunction();
:
cmp QWORD PTR [rbp-24], 0
je .L20
mov rax, QWORD PTR [rbp-24]
mov rax, QWORD PTR [rax]
sub rax, 24
mov rax, QWORD PTR [rax]
mov rdx, rax
mov rax, QWORD PTR [rbp-24]
add rax, rdx
jmp .L21
.L20:
mov eax, 0
.L21:
cmp QWORD PTR [rbp-24], 0
cmp QWORD PTR [rbp-24], 0
je .L24
mov rdx, QWORD PTR [rbp-24]
mov rdx, QWORD PTR [rdx]
sub rdx, 24
mov rdx, QWORD PTR [rdx]
mov rcx, rdx
mov rdx, QWORD PTR [rbp-24]
add rdx, rcx
jmp .L25
.L24:
mov edx, 0
.L25:
mov rdx, QWORD PTR [rdx]
add rdx, 8
mov rdx, QWORD PTR [rdx]
mov rdi, rax
call rdx