C++ Virtual Function

简单介绍了C++虚函数的基本概念和用法及其底层的实现原理

C++虚函数

基本概念与用法

虚函数是应在派生类中重新定义的成员函数。 当使用指针或对基类的引用来引用派生的类对象时,可以为该对象调用虚函数并执行该函数的派生类版本。虚函数是动态绑定的,所以不能将静态函数声明为虚函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
using namespace std;
class A
{
public:
void foo1() //nonvirtual function
{
cout << "in A::foo1" << endl;
}
virtual void foo2() //define a virtual function
{
cout << "in A::foo2" << endl;
}
};
class B: public A
{
public:
void foo1() //nonvirtual function
{
cout << "in B::foo1" << endl;
}
virtual void foo2() //virtual function
//virtual is optional explanation
{
cout << "in B::foo2" << endl;
}
};
void func(A& a)
{
cout << "in func: ";
a.foo2(); //call virtual function
}
int main()
{
A* b = new B();
b->foo1(); //call nonvirtual function
b->foo2(); //call virtual function
}

基类的虚函数必须定义,除非被声明为纯虚函数,即基类为抽象类。

基类当定义好后虚函数后,则在基类所有的派生类中,此函数都是虚函数,若没有被重载就会默认使用该派生类父类所重载的虚函数。

若要重载该虚函数,可以显式地使用virtual关键词(但不必要)来重载此虚函数。

派生类的虚函数的返回类型,参数列表需要同基类的虚函数相同,但也有例外情况,若基类返回的是类本身的指针或引用时,上述规则无效。

final 与 override

C++11后,新增了两个后缀说明符finaloverride, override是可选的,用来显式说明该虚函数是被重载的,而final用来说明该类的虚函数不能被其派生类重载。

回避虚函数的机制

若想指定地执行虚函数的某个版本,可以使用作用域运算符来实现,例如

1
2
3
A* test = new B();
test->foo2(); //call B::foo2()
test->A::foo2(); //call A::foo2()

注:此调用是在编译时刻完成的。

虚函数的实现

  1. 每一个class产生出一堆指向virtual function 的指针,放在表格之中。这个表格被称为virtual table (vtbl)。

  2. 每一个class object 被安插了一个指针,指向相关的virtual table。通常这个指针被称为vptr

    未命名文件

在用派生类初始化基类的引用和指针时,编译器仅会修改指针的类型(即指针所指的内存的大小和其内容),但不会修改类中的vptr,所以类中的vptr指向的还是派生类的vtbl。注意指针所涵盖的地址只包含派生类中的派生于基类的成员,你不能通过其访问派生类特有的成员,但是你可以通过调用virtual function来访问派生类特有的成员。

在使用基类构造函数转化派生类时,会将vptr重新设定为基类的vptr,并不会沿用派生类的vptr,并对派生类的成员进行截断,只保留基类的成员,此时不会展现多态性。