本文首发于个人博客
虚函数
- C++中的多态通过虚函数(virtual function)来实现
- 虚函数:被virtual修饰的成员函数
- 只要在父类中声明为虚函数,子类中重写的函数也自动变成虚函数(也就是说子类中可以省略virtual关键字)
先看一个例子
1 | class Person{ |
我们用Person
指针指向Student
对象,然后真正输出的是Person::run()
1 | int main(){ |
输出
Person::run()
原因
- 那是因为对于编译器来说,编译代码的时候,发现
stu
指针是Person类型,那么调用的时候,直接调用了Person::run()
。有没有办法调用Student::run()
呢?答案是有的,就是用virtual
修饰 - 用
virtual
修饰的函数,是虚函数
1 | class Person{ |
改成如上代码之后,输出结果为
Student::run()
虚表
- 虚函数的实现原理是虚表,这个虚表里面存储着最终需要调用的虚函数地址,这个虚表也叫虚函数表
对于上面的例子中,Student
对象的前4个字节存放的是指向虚表的地址
当我们调用的时候,
1 | Person *stu = new Student(); |
会首先把虚表地址取出来,然后去虚表中调用Student::run()
所有的Student对象(不管在全局区、栈、堆)共用同一份虚表
1 | 0x1000010e5 <+53>: movq -0x18(%rbp), %rax |
跟踪汇编代码调用如下的Student::run:
1 | C++test01`Student::run: |
纯虚函数
- 纯虚函数:没有函数体且初始化为0的虚函数,用来定义接口规范
- 抽象类(Abstract Class)
- 含有纯虚函数的类,不可以实例化(不可以创建对象)
- 抽象类也可以包含非纯虚函数、成员变量
- 如果父类是抽象类,子类没有完全重写纯虚函数,那么这个子类依然是抽象类
例如下面的Person
类就是一个含有纯虚函数的类,可以用来定义接口规范
1 | class Person{ |
- 定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。
- 纯虚函数的意义,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的默认实现。
- 所以类纯虚函数的声明就是在告诉子类的设计者,”你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。