虚函数语法形式
class Foo{
...
virtual void func();
...
};
C++虚函数必须和C++的继承结合起来一起看。示例是最好的解释。
#include <iostream>
using namespace std;
class Base{
public:
void func1(){
cout<<"I'm Base func1"<<endl;
}
virtual void func2(){
cout<<"I'm Base func2"<<endl;
}
};
class Derived:public Base{
public:
void func1(){
cout<<"I'm Derived func1"<<endl;
}
virtual void func2(){
cout<<"I'm Derived func2"<<endl;
}
};
int main(){
Derived d= Derived();
Base &o = d; //创建了一个指向子类对象的引用
o.func1(); //func1不是虚函数,因此使用了Base的func1方法
o.func2(); //func2是虚函数,因此根据引用所指向的实际对象的类型选择func2,即Derived的func2
return 0;
}
运行结果
I'm Base func1
I'm Derived func2
汇编代码分析
40091c: 48 c7 45 f0 00 00 00 movq $0x0,-0x10(%rbp) #-0x10(%rbp)中存放的是对象d,这个对象占用8个字节
400923: 00 #这8个字节将会用来存储一个指针,指针指向Derived类的虚函数表
400924: 48 8d 45 f0 lea -0x10(%rbp),%rax
400928: 48 89 c7 mov %rax,%rdi
40092b: e8 18 01 00 00 callq 400a48 <_ZN7DerivedC1Ev> #调用Derived的默认构造函数
400930: 48 8d 45 f0 lea -0x10(%rbp),%rax
400934: 48 89 45 f8 mov %rax,-0x8(%rbp) #-0x8(%ebp)存储引用o,o的内容实际上是对象d的起始地址。
400938: 48 8b 45 f8 mov -0x8(%rbp),%rax
40093c: 48 89 c7 mov %rax,%rdi #这两行准备this指针
40093f: e8 70 00 00 00 callq 4009b4 <_ZN4Base5func1Ev #不是虚函数,直接调用Base的func1
400944: 48 8b 45 f8 mov -0x8(%rbp),%rax #取出d的地址
400948: 48 8b 00 mov (%rax),%rax #取出d的内容,即指向Derived类虚函数表的指针
40094b: 48 8b 10 mov (%rax),%rdx #func2是Derived类的唯一虚函数,即第0项。取出func2的地址。
40094e: 48 8b 45 f8 mov -0x8(%rbp),%rax
400952: 48 89 c7 mov %rax,%rdi #这两行也是准备this指针
400955: ff d2 callq *%rdx #调用func2
如果感兴趣,可以用gdb实际调试一下,观察运行的过程。
NOTE 1:当子类不override父类的虚函数,那么子类就自动继承父类的虚函数。
NOTE 2:虚函数也可以有默认参数。如果用父类的指针或引用指向子类的对象,那么调用的函数是子类的,使用的默认参数却是父类函数的。(C++真是无比混乱啊!)
NOTE 3: 如果不想动态调用虚函数,可以使用类名+'::'来限定调用哪个函数。例如,上例中可以添加语句o.Base::func2()来调用父类的func2。
原文链接: https://www.cnblogs.com/richardustc/archive/2013/03/29/2988449.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/82480
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!