深入了解C++(2)——虚函数与虚函数底层实现

 面向对象程序设计(object-oriented programming)是一个非常重要的概念,其核心思想是数据抽象、继承、动态绑定。在C++语言中,虚函数的作用是实现多态性。而纯虚函数则作为一个没有具体实现的虚函数,与java程序设计的influence非常相似。今天本篇文章将简要总结C++中的虚函数,并且深入研究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
class A
{
public:
virtual void output(string s)
{
cout<<"A(output):"<<s<<endl;
}
};

class B:public A
{
virtual void output(string s)
{
cout<<"B(output):"<<s<<endl;
}
};

int main()
{
A* ptr=new B();
ptr->output("hello");
delete ptr;
}

 基于上述的代码,我们将得到“B(output):hello”的输出,注意一旦我们定义一个函数为虚函数,那么它将一直作为虚函数,子类无法改变该函数是虚函数这个事实。

二 纯虚函数

 在C++中,如果一个类包含纯虚函数,那么这个类将成为一个抽象类。抽象类无法创建出对象,只有继承了抽象类的子类通过实现虚函数才能被实例化。纯虚函数的概念与java中的influence非常相近,它是一种只提供声明不提供实现的约束。子类需要一一将其全部实现。一个常见的纯虚函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
class A
{
public:
virtual void output(string s)=0;
};

class B:public A
{
virtual void output(string s)
{
cout<<"B(output):"<<s<<endl;
}
};

 如果不想实现一个纯虚函数,那么继承它的类也将是一个抽象类。这个范例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A
{
public:
virtual void output(string s)=0;
};

class B:public A
{
virtual void output(string s)=0;
};

class C:public B
{
virtual void output(string s){
cout<<"C(output):"<<s<<endl;
}
};

三 动态绑定

 在了解虚函数的底层实现之前,首先需要了解的是动态绑定概念。

  • 在执行期间(非编译期)判断所引用对象的实际类型,根据实际类型(动态类型)调用相应的方法。
  • 动态绑定灵活性相对静态绑定来说要高,因为它在运行之前可以进行选择性的绑定,但同时,动态绑定的执行效率要低些,因为绑定对象还要进行编译(现在编译期一般都会多次编译)。


    触发动态绑定的条件:
  • (1)只有指定为虚函数的成员函数才能进行动态绑定;
  • (2)只有通过基类的指针或引用进行函数调用。

 了解了这个关键点后,我们发现我们需要去了解的重点是,如何进行动态绑定的概念,此时就涉及到了C++虚函数的底层实现。

四 虚函数的底层实现

 简单查阅资料我们可以发现,C++虚函数的实现主要利用了虚函数表。编译器将为实现了虚函数的基类和覆盖了虚函数的派生类分别创建一个虚函数表(Virtual Function Table,VFT)。也就是说Base和Derived类都将有自己的虚函数表。实例化这些类的对象时,将创建一个隐藏的指针VFT*,它指向相应的VFT。可将VFT视为一个包含函数指针的静态数组,其中每个指针都指向相应的虚函数。Base类和Derived类的虚函数表如下图所示:

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
class Base
{
public:
virtual void Func1()
{
//Func1的实现代码
}
virtual void Func2()
{
//Func2的实现代码
}
//Func3、Func4等虚函数的实现
virtual void FuncN()
{
//FuncN的实现代码
}
};
class Derived:public Base
{
public:
virtual void Func1()
{
//Func2覆盖Base类的Func1代码
}
//除去Func2的其他所有虚函数的实现代码
virtual void FuncN()
{
//FuncN覆盖Base类的FuncN代码
}
};

继承关系示意图

 如上图所示,利用上述代码编译后,编译器将会为base类和derived类提供虚函数表。当一个对象被实例化时,将会创建一个隐藏的指针指向对应的虚函数表,从而实现运行时多态。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×