[面试经]VPTR和VTBL

假设是32位机器。

在这里,解释一下虚函数表(Virtual table 或称为vtbl),虚函数指针( Virtual pointer 或者称为 vptr)的内部工作原理。

首先,我们必须了解一下内存布局。

例一:类的内存布局

#include <iostream>
using namespace std;

class Test{
public:
    int data1;
    int data2;
    int fun1();
};

int main(){
    Test obj;
    cout << "obj's Size = " << sizeof(obj) << endl;
    cout << "obj 's Address = " << &obj << endl;
    return 0;
}

输出结果:

Sobj's Size = 8
obj 's Address = 0012FF7C

注意:任何的成员函数都不会占用任何的内存。

例二 :派生类的内存布局

// 父类
class Test{
public:
    int a;
    int b;
};
// 派生类
class dTest : public Test{
public:
    int c;
};



int main(){

    Test obj1;
    cout << "obj1's Size = " << sizeof(obj1) << endl;
    cout << "obj1's Address = " << &obj1 << endl;

    dTest obj2;
    cout << "obj2's Size = "<< sizeof(obj2) << endl;
    cout << "obj2's Address = "<< &obj2 << endl;
    return 0;
}

输出结果:

obj1's Size = 8
obj1's Address = 0012FF78
obj2's Size = 12
obj2's Address = 0012FF6C

例三 : 带有虚函数类的内存布局

// 带有虚函数的类
class Test{
public:
    int data;
    // 虚函数
    virtual void fun1(){
        cout << "Test::fun1" << endl;
    }
};

int main(){

    Test obj;
    cout << "obj's Size = " << sizeof(obj) << endl;
    cout << "obj's Address = " << &obj << endl;
    return 0;
}

输出结果:

obj's Size = 8
obj's Address = 0012FF7C

注意:在类中添加虚函数会产生额外的负担(4Byte)

例四:不止一个虚函数

class Test{
public:
    int data;
    virtual void fun1() { cout << "Test::fun1" << endl; }
    virtual void fun2() { cout << "Test::fun2" << endl; }
    virtual void fun3() { cout << "Test::fun3" << endl; }
    virtual void fun4() { cout << "Test::fun4" << endl; }
};

int main(){

    Test obj;
    cout << "obj's Size = " << sizeof(obj) << endl;
    cout << "obj's Address = " << &obj << endl;
    return 0;
}

输出结果:

obj's Size = 8
obj's Address = 0012FF7C

注意:如果一个类中有虚函数的前提下,再增加虚函数就不会产生额外的内存负担。仍然产生额外的4Byte负担。

例五:

class Test{
public:
    int a;
    int b;
    Test(int temp1 = 0, int temp2 = 0){
        a=temp1;
        b=temp2;
    }
    int getA(){
        return a;
    }
    int getB(){
        return b;
    }
    virtual ~Test();
};

int main(){

    Test obj(5, 10);

    // Changing a and b
    int* pInt = (int*)&obj;
    *(pInt+0) = 100;
    *(pInt+1) = 200;

    cout << "a = " << obj.getA() << endl;
    cout << "b = " << obj.getB() << endl;
    return 0;
}

输出结果:

a = 200
b = 10

如果作如下修改:

// Changing a and b
int* pInt = (int*)&obj;
*(pInt+1) = 100; // In place of 0
*(pInt+2) = 200; // In place of 1

输出结果:

a = 100
b = 200

注意:位于class第一位置是vptr,其他都位于其后面。
(Who sits 1st place of Class : Answer is VPTR,VPTR - 1st placed in class and rest sits after it. )

例六:

class Test {
    virtual void fun1(){
        cout << "Test::fun1" << endl;
    }
};

int main(){

    Test obj;
    cout << "VPTR's Address " << (int*)(&obj+0) << endl;
    cout << "VPTR's Value " << (int*)*(int*)(&obj+0) << endl;
    return 0;
}

输出结果:

VPTR's Address 0012FF7C
VPTR's Value 0046C060

注意:VPTR’s Value是Virtual table的地址,让我们下面的例子。

例七:

class Test{
    virtual void fun1(){
        cout << "Test::fun1" << endl;
    }
};

typedef void (*Fun)(void);

int main(){

    Test obj;
    cout << "VPTR's Address " << (int*)(&obj+0) << endl;
    cout << " VIRTUAL TABLE 's Address " << (int*)*(int*)(&obj+0) << endl; // Value of VPTR
    cout << "Value at first entry of VIRTUAL TABLE " << (int*)*(int*)*(int*)(&obj+0) << endl;

    Fun pFun = (Fun)*(int*)*(int*)(&obj+0);   // calling Virtual function
    pFun();
    return 0;
}

输出结果:

VPTR's Address 0012FF7C
VIRTUAL TABLE 's Address 0046C0EC
Value at first entry of VIRTUAL TABLE 0040100A
Test: fun1

例八:

class Test{
    virtual void fun1() { cout << "Test::fun1" << endl; }
    virtual void func1() { cout << "Test::func1" << endl; }
};

int main(){

    Test obj;

    cout << "VPTR's Address " << (int*)(&obj+0) << endl;
    cout << "VIRTUAL TABLE 's Address"<< (int*)*(int*)(&obj+0) << endl;

    // Calling Virtual table functions
    cout << "Value at 1st entry of VTable " << (int*)*((int*)*(int*)(&obj+0)+0) << endl;
    cout << "Value at 2nd entry of VTable " << (int*)*((int*)*(int*)(&obj+0)+1) << endl;

    return 0;
}

输出结果:

VPTR's Address 0012FF7C
VIRTUAL TABLE 's Address 0046C0EC
Value at first entry of VIRTUAL TABLE 0040100A
Value at 2nd entry of VIRTUAL TABLE 004012

例九:

class Test{
    virtual void fun1() { cout << "Test::fun1" << endl; }
    virtual void func1() { cout << "Test::func1" << endl; }
};

typedef void(*Fun)(void);

int main(){

    Test obj;
    Fun pFun = NULL;

    // calling 1st virtual function
    pFun = (Fun)*((int*)*(int*)(&obj+0)+0);
    pFun();

    // calling 2nd virtual function
    pFun = (Fun)*((int*)*(int*)(&obj+0)+1);
    pFun();

    return 0;
}

输出结果:

Test::fun1
Test::func1

例十:

class Base1{
public:
    virtual void fun();
};

class Base2{
public:
    virtual void fun();
};

class Base3{
public:
    virtual void fun();
};

class Derive : public Base1, public Base2, public Base3{
};

int main(){

    Derive obj;
    cout << "Derive's Size = " << sizeof(obj) << endl;
    return 0;
}

输出结果:

Derive's Size = 12

例十一:

class Base1{
    virtual void fun1() { cout << "Base1::fun1()" << endl; }
    virtual void func1() { cout << "Base1::func1()" << endl; }
};

class Base2 {
    virtual void fun1() { cout << "Base2::fun1()" << endl; }
    virtual void func1() { cout << "Base2::func1()" << endl; }
};

class Base3 {
    virtual void fun1() { cout << "Base3::fun1()" << endl; }
    virtual void func1() { cout << "Base3::func1()" << endl; }
};

class Derive : public Base1, public Base2, public Base3{
public:
    virtual void Fn(){
        cout << "Derive::Fn" << endl;
    }
    virtual void Fnc(){
        cout << "Derive::Fnc" << endl;
    }
};

typedef void(*Fun)(void);

int main(){

    Derive obj;
    Fun pFun = NULL;

    // calling 1st virtual function of Base1
    pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+0);
    pFun();

    // calling 2nd virtual function of Base1
    pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+1);
    pFun();

    // calling 1st virtual function of Base2
    pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+0);
    pFun();

    // calling 2nd virtual function of Base2
    pFun = (Fun)*((int*)*(int*)((int*)&obj+1)+1);
    pFun();

    // calling 1st virtual function of Base3
    pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+0);
    pFun();

    // calling 2nd virtual function of Base3
    pFun = (Fun)*((int*)*(int*)((int*)&obj+2)+1);
    pFun();

    // calling 1st virtual function of Drive
    pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+2);
    pFun();

    // calling 2nd virtual function of Drive
    pFun = (Fun)*((int*)*(int*)((int*)&obj+0)+3);
    pFun();

    return 0;
}

输出结果:

Base1::fun
Base1::func
Base2::fun
Base2::func
Base3::fun
Base3::func
Drive::Fn
Drive::Fnc

顺便提一下函数指针:

int i;
定义了一个int类型的变量i;
而这样
typedef INT int;
表示用户自己定义了一个整型数据类型INT,实际上就等同于int
所以:INT ii;
同上,表示定义了一个int类型的变量ii;

同理可得:
typedef void (*Fun)(void);
表示用户自己定义了一个函数指针数据类型 ,该函数指针指向 类似void Foo(void)函数的函数入口地址
Fun pf;
表示定义了一个函数指针pf,该函数指针指向类似于void *pf(void)的函数 //leo

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值