C++虛函數調用規則
基類、派生類結構:
class Foo {
public:virtual void print() {cout << "Foo" << endl;}
};
class Bar : public Foo {
public:virtual void print() {cout << "Bar" << endl;}
};
1.通過對象直接調用虛函數
通過對象直接調用虛函數,不涉及到虛函數表指針的訪問(g++,MSVC,clang++測試結果一致)
Foo foo;
foo.print();
Bar bar;
bar.print();
foo.print()與bar.print()對應的匯編:
# foo.print
lea rdi, [rbp - 16]
call Foo::print()
# bar.print
lea rdi, [rbp - 24]
call Bar::print()
2.通過基類的引用調用虛函數
通過基類的引用調用虛函數,不同編譯器的表現不同
Foo foo;
Bar bar;
Foo& r1=foo;
r1.print();
Foo& r2=bar;
r2.print();
-
clang編譯器,不管引用的是基類對象還是派生類對象,調用虛函數都要訪問虛函數表指針
#r1.print: mov rax, qword ptr [rdi] call qword ptr [rax] #r2.print: mov rax, qword ptr [rdi] call qword ptr [rax]
-
g++編譯器,當引用基類對象時,調用虛函數不涉及到虛函數表指針的訪問,引用派生類對象時,調用虛函數涉及到虛函數表指針的訪問
#r1.print: ldr r0, [r7, #12] bl Foo::print() #r2.print: ldr r3, [r7, #8] ldr r3, [r3] ldr r3, [r3] ldr r0, [r7, #8] blx r3
補充:使用派生類的引用調用虛函數
Bar bar;
Bar& r=bar;
r.print();
g++編譯器:不涉及虛函數表指針的訪問
ldr r0, [r7, #4]
bl Bar::print()
clang編譯器:涉及到虛函數表指針的訪問
mov rax, qword ptr [rdi]call qword ptr [rax]
3.使用指針調用虛函數
使用指針調用虛函數時,均涉及到虛函數表指針的訪問(在g++和clang編譯器下表現相同)
Foo foo;
Bar bar;
Foo* p1=&foo;
Foo* p2=&bar;
Bar* p3=&bar;
p1->print();
p2->print();
p3->print();
p1、p2、p3調用print函數的匯編:
mov rax, qword ptr [rdi]
call qword ptr [rax]
mov rdi, qword ptr [rbp - 40]
mov rax, qword ptr [rdi]
call qword ptr [rax]
mov rdi, qword ptr [rbp - 48]
mov rax, qword ptr [rdi]
call qword ptr [rax]