优秀的编程知识分享平台

网站首页 > 技术文章 正文

C++虚函数动态绑定实现原理(c调用c++动态库 虚函数)

nanyue 2024-10-18 07:35:03 技术文章 26 ℃

通过指针、引用来调用一个虚函数,实际被调用的函数到运行时才能确定,似乎有一种神秘感,使得我们不能用思考普通函数的方式去思考虚函数。对于初学者来说,对虚函数保持这种神秘感倒也无妨,只要清楚它的用法就好了,但是若是想要“究其原理”也并不是一件困难的事情。

动态绑定的关键是,在运行时决定被调用的函数。

这个要求很容易让我们想起函数指针。一个函数指针可以被赋予不同函数的入口地址,如果通过函数指针去调用函数,实际被调用的函数一般到了运行的时候才能确定,因此通过函数指针去调用函数,也是一种动态绑定,是一种由源程序进行的显示控制的动态绑定。而虚函数的动态绑定,一些控制细节被隐藏了起来,由编译器自动处理了。

编译器实现虚函数的动态绑定的细节并没有在C++标准中规定,So会因编译器而异。

下面以下列代码为例,进行讨论

class Base{ //基类定义

public:

virtual void f(); //基类的虚函数f()

virtual void g(); //基类的虚函数g()

private:

int i; //基类的数据成员

};

class Derived:public Base{ //派生类定义

public:

virtual void f(); //覆盖基类的虚函数f()

virtual void h(); //声明派生类的新的虚函数h()

private:

int j; //派生类的数据成员

};

注:Base类和Derived类成员函数的实现略去。

首先介绍一种直接的处理方法:

在每个对象中,除了存储数据成员外,还为每个虚函数设置一个函数指针,分别存放这些虚函数对应的代码的入口地址。由于派生类也要继承这些虚函数的接口,因此也要保留这些指针,而把派生类中引入的新的数据成员和函数指针置于从基类继承下来的数据成员和函数指针后。在各个类的构造函数中,为各个函数指针初始化,使得基类对象中的函数指针指向为基类定义的函数,派生类对象中的函数指针指向派生类覆盖后的函数。在通过指针或引用进行函数调用时,先读取相应的函数指针,再通过函数指针调用相应的函数。(可以结合下图理解)

如图所示,由于Derived类覆盖了Base类的f 函数,因此Base对象和Derived对象中为f 函数设置的指针,指向不同的函数代码;Derived类未覆盖g函数,因此两个类的对象中为g函数设置的指针指向相同的代码;至于h函数,它是派生类新增的函数,只有Derived对象中有为它准备的指针。


这种方式存在一个致命的问题——占用的额外空间太大,例如每一个Base对象都要占用两个指针的额外空间,每个Derived 对象要占用3个指针的额外空间。所以这并不是被广泛采用的方法,之所以将它提出仅仅是因为它简单,直接,利于理解,只要在此基础上再深入一点便能得到更好的处理方法了。明天我们再来一起分析一下更好的处理方法

Tags:

最近发表
标签列表