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]