首页 > 代码库 > C++跟我一起透彻理解虚函数表

C++跟我一起透彻理解虚函数表

//首先让我们来了解类对象的构造顺序。
#include <iostream>
using namespace std;
class A
{
public:
    A(){ cout << "A" << endl; }
    virtual void PrintfA() = 0;
};
class B 
{
public:
    B(){ cout << "B" << endl; }
};
class C :virtual public A , public B
{
    //关于构造对象顺序:
    //第一步先构造虚基类,比如此处的虚继承。

//第二步构造一般继承类。此处从左往右顺序运行。 //第三部构造成员变量,记住这里的成员变量的构造是顺序运行的。

//第四构造自身的构造函数。

public: C() { cout << "C" << endl; } void PrintfA() { cout << "hello word!!" << endl; } private: }; int main() { C c; return 0; }

技术分享

#include <iostream>
using namespace std;

typedef void (*Pfun)();
class Base
{
public:
    virtual void Printf()
    {
        cout << "Base::Printf()" << endl;
    }
    void PrintfBae()
    {
        cout << "Base::PrintfBase" << endl;
    }
};
class Son1:public Base
{
public:
    virtual void Printf()
    {
        cout << "Son1::Printf()" << endl;
    }
    void PrintfSon1()
    {
        cout << "Son1::PrintfSon1()" << endl;
    }
};
class Son2 : public Son1
{
public:
    void Printf()
    {
        cout << "Son2::Printf()" << endl;
    }
    virtual void hello()
    {
        cout << "hello" << endl;
    }
    void PrintfSon2()
    {
        cout << "Son2::Printf()" << endl;
    }
};
int main()
{
    Son2 s2;
    Base &b = s2;
    //b.Printf();
    //此处能够得出这三个类维护了一个共同的虚表,可是我们还无法知道他们的
    //详细位置以及各个类里面非虚函数的存储关系。所以我们接下来验证。

//Pfun fun = (Pfun)*((int *)(&(int*)&b)+1); Pfun fun = (Pfun)*((int *)*(int *)(int *)(&b) + 0); //这样的做法基本没有人解释,所以我在后面用图解释了。 fun(); fun = (Pfun)*((int *)*(int *)(int *)(&b) + 1); fun(); //-------------------------------------------------------- fun = (Pfun)*((int *)*(int *)(int *)(&s2) + 0); fun(); fun = (Pfun)*((int *)*(int *)(int *)(&b) + 1); fun(); //打印结果与上面一样,能够得出这份虚表是全部继承该拥有虚表基类的成员类所共享的。 //每个对象的产生必然有两个数据。一个vptr指向虚表的指针,还有其自身的成员 //变量。

return 0; } //总结1:前提是基类是有虚表的,单一继承,全部的成员类与基类共同维护同一个虚表,各自有 //一个vptr指向这个虚表,子类假设会对虚表进行替换改动,详细会依据子类是否重载了基类的 //虚函数来确定,我们成为覆盖。

多继承以下讨论,事实上本质是一样的,关键问题是,多继承中 //子类中究竟有几份虚vptr指针。

技术分享

#include<iostream>
using namespace std;

class A
{
public:
    virtual void Printf()
    {
        cout << "A::Printf()" << endl;
    }
    virtual void PrintfA()
    {
        cout << "A::PrintfA()" << endl;
    }
};
class B
{
public:
    virtual void PrintfB()
    {
        cout << "B::Printf()" << endl;
    }
    virtual void Printf()
    {
        cout << "B::Printf()" << endl;
    }
};
class C : public A, public B
{
public:
    void Printf()
    {
        cout << "C::Printf()" << endl;
    }
};

int main()
{
    typedef void(*PFun)();
    C c;
    PFun fun = (PFun)*((int *)*(int *)((int *)(&c)+0) + 0);//第一个虚表指针vptr。

fun();//此处覆盖了。调用的是C中的Printf(); fun = (PFun)* ((int *)*(int *)((int *)(&c) + 1) + 0);//第二个虚表指针vptr。 fun(); //此处另一个亮点,就是为什么(int *)(int *)两次。这下你应该明确了吧。由于可能 //有多个虚指针。编译器做的非常好了,假设不这样做编译会报错。

cout << sizeof(c) << endl;//终极測试结果8。 return 0; } //总结2:多继承中每个基类都维护一张自己的虚表,而子类中每继承一个虚基类就会有存在一个 //指向该基类虚表的指针,以上代码測试结果是C对象中有两个vptr指针。一个指向A基类的虚表,一 //个指向B基类的虚表。

技术分享

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

C++跟我一起透彻理解虚函数表