c++多态和虚函数表实现原理

 

自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:

https://www.cnblogs.com/bclshuai/p/11380657.html

C++多态以及虚函数表实现原理

 

目录

1      定义

2      虚函数表实现原理

3      实例解析

3.1     定义父类

3.2     父类对象地址空间剖析

3.3     子类继承父类的虚函数表

3.3.1     单继承覆盖和不覆盖对比

3.3.2     子类继承多个父类

3.3.3     多层继承的子类

1        定义

C++中的虚函数的作用主要是实现了多态的机制。关于多态,子类对象赋值给父类指针,然后通过父类的指针调用实际子类覆盖父类的虚函数实现。赋值不同的子类对象给父类指针,可以使用父类指针调用不同子类的虚函数(必须是覆盖父类的函数),这种技术可以让父类的指针有“多种形态”。

2        虚函数表实现原理

每个子类对象创建时,会有一个虚函数表,而且虚函数表在对象首地址开始,以达到高效访问的目的。一个子类继承多个父类时,子类对象的头部有多个虚函数表,把父类的虚函数表复制过来,按照继承顺序排列。如果子类覆盖了父类的虚函数,则替换掉复制过来的父类虚函数表中对应位置的虚函数。子类中的自定义虚函数,在第一个虚函数表中,排在父类虚函数后面(主流说法是这样,但是实际测试,并没有)。当用子类的对象赋值给父类指针时,父类指针指向了子类虚函数表,当调用函数时,就会从子类虚函数表中查找函数,去调用子类覆盖父类的虚函数,这样就实现了多态。

3        实例解析

3.1   定义父类

class Base {

            public:

            virtual void f() { cout << "Base::f" << endl; }

            virtual void g() { cout << "Base::g" << endl; }

            virtual void h() { cout << "Base::h" << endl; }

int a=0;

};

3.2   父类对象地址空间剖析

创建一个对象Base b;

父类的虚函数表示意图为

 

 虚函数表在对象地址首部,函数指针按照定义的顺序在虚函数表中,可以通过地址访问的方式去访问这些函数。

int main()

{

       typedef void(*Fun)(void);

 

       Base b;

       cout << "虚函数表地址:" << (int*)(&b) << endl;

       cout << "1" << *(int*)(&b) << endl;

       cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;

       Fun funf = ((Fun)*((int*)*(int*)(&b) + 0));

       Fun fung= ((Fun)*((int*)*(int*)(&b) + 1));

       Fun funh= ((Fun)*((int*)*(int*)(&b) + 2));

       funf();

       fung();

       funh();

       system("pause");

 

    return 0;

}

输出结果

 

b地址空间如下图所示:

理解下面一句是怎么获取到函数地址的。

pFun = (Fun)*((int*)*(int*)(&b));

(&b)获取到对象b的地址。

(int*)(&b))转化为int*的指针,对象b的前4个字节是虚函数表的地址,int* 也是四个字节,这种强制转换,是把&a开始的4个字节当作一个整体,也就是虚函数表的地址,虚函数表地址在对象地址空间首部。

 *(int*)(&b))  *虚函数表地址,得到的是虚函数表的内存空间,虚函数表内存空间首部是虚函数f的地址,即Base::f()的地址。

 ((int*)*(int*)(&b))在转换为int*类型的指针,就得到了四个字节的虚函数f的地址。f,g,h的地址各占四个字节,int*的指针+1,就是向后移动四个字节,得到g函数的地址,+2,就是再向后移动四个字节,得到h函数的地址。

*((int*)*(int*)(&b))  *虚函数地址,得到的是虚函数f的实际的存储空间。

(Fun)*((int*)*(int*)(&b));转换为函数指针。

 

思考:如果虚函数fgh是private私有的,那么通过这种方式仍然可以获取到私有函数的地址fgh,并且去访问这些私有函数。

3.3   子类继承父类的虚函数表

3.3.1         单继承覆盖和不覆盖对比

class Derver: public Base{

public:

       virtual void f1() { cout << "Derver::f" << endl; }

 

       virtual void g1() { cout << "Derver::g" << endl; }

 

       virtual void h1() { cout << "Derver::h" << endl; }

};

查看无覆盖和有覆盖继承的情况,右边子类覆盖了父类的f函数。

 

无覆盖的子类对象地址空间。网上说父类虚函数在虚表前面,子类虚函数在后面

 

但是实际情况是虚函数表中只有父类虚函数,如下所示

 

有覆盖的子类对象地址空间,子类虚函数表的首位替换了子类覆盖的函数

 

虚函数表中是子类虚函数替换了父类虚函数的位置,也没有未覆盖的子类虚函数f1和g1。

 

总结:单继承的子类对象虚函数表中,只包含父类对象的虚函数和覆盖的子类虚函数,子类虚函数会替换掉对应位置的父类虚函数。子类自定义的虚函数不在虚函数表中。

3.3.2         子类继承多个父类

(1)   定义一个子类继承三个基类,继承关系如下图

 

代码实现

class Base1 {

public:

       virtual void f() { cout << "Base1::f" << endl; }

 

       virtual void g() { cout << "Base1::g" << endl; }

 

       virtual void h() { cout << "Base1::h" << endl; }

};

class Base2 {

public:

       virtual void f() { cout << "Base2::f" << endl; }

 

       virtual void g() { cout << "Base2::g" << endl; }

 

       virtual void h() { cout << "Base2::h" << endl; }

};

class Base3 {

public:

       virtual void f() { cout << "Base3::f" << endl; }

 

       virtual void g() { cout << "Base3::g" << endl; }

 

       virtual void h() { cout << "Base3::h" << endl; }

};

class Mult :public Base1, public Base2,public Base3 {

public:

       virtual void f() { cout << "Mult::f" << endl; }

 

       virtual void g1() { cout << "Mult::g" << endl; }

 

};

class Grander :public Derver

{

public:

       virtual void f1() { cout << "Grander::f" << endl; }

 

       virtual void g1() { cout << "Grander::g1" << endl; }

};

(2)   查看子类对象的地址空间

如下图所示,子类对象空间有三个基类虚表,子类覆盖的函数f替换了虚表中对应的位置。子类自有的虚函数网上说是在第一个虚表后面。但是实际却没有。

 

如下图所示,是实际的地址空间,第一个虚表中并没有看到子类的虚函数g1.

 

总结:子类继承多个父类,子类对象中包含了三个父类的虚函数表,按照继承顺序排列。子类覆盖的虚函数会替换对应位置的三个父类虚函数。子类自定义的虚函数也不在虚函数表中。

3.3.3         多层继承的子类

子类Derver继承父类Base,然后Grander类继承Derver类。

代码实现如下

class Base {

private:

    virtual void f() { cout << "Base::f" << endl; }

 

    virtual void g() { cout << "Base::g" << endl; }

 

    virtual void h() { cout << "Base::h" << endl; }

    int a = 0;

};

class Derver: public Base{

public:

    virtual void f() { cout << "Derver::f" << endl; }

    virtual void g1() { cout << "Derver::g1" << endl; }

    virtual void h1() { cout << "Derver::h1" << endl; }

    int d =1;

};

class Grander :public Derver

{

public:

    virtual void f() { cout << "Grander::f" << endl; }

    virtual void g1() { cout << "Grander::g1" << endl; }

};

内存虚表结构如下所示,只虚表中只有有Base类中函数Base::g(),Base::h(),还有覆盖的子类函数Grander::f()。其他的函数都没有。连覆盖Derver的Grander::g1()都没有。这是什么鬼?

 

总结:多层继承,子类对象的虚函数表中值包含Base类的虚函数,子类覆盖的虚函数替换掉对应位置的Base类虚函数。中间的Derver虚函数和Grander中自定义的虚函数不存在虚函数表中。

(1)基类Base指针指向对象Derver

Base* pB = new Derver();

得到的内存结构如下图所示

有两个虚拟表,虚函数表中地址相同,函数是基类的虚函数和Derver覆盖基类的虚函数。

 

(2)中间的指针Derver*指向孙类对象Grander

得到的虚表结构如下,虚函数是基类base虚函数和Grander覆盖基类的虚函数Grander::f();

Derver* pD  = new Grander();

 

(3)基类Base指针指向孙类对象Grander

得到的虚函数表如下图所示

Base* pDG = new Grander();

 

总结:多层继承时,虚函数表中保存的是基类的虚函数Base和覆盖基类的虚函数。虚函数表会保留继承层级关系。会有两个虚函数表,表中的函数地址相同。

 

原文地址:https://www.cnblogs.com/bclshuai/p/13947597.html