虚函数、虚表的生成,虚表的修改

接上文。
虚函数、虚表在没有实例的情况下是无法从语法层面进行访问的。
那么其到底有没有生成呢?

#include<iostream>
using namespace std;

class A
{
private:
    int x;
    long long xy;
    int y;

public:
    virtual void f1(void)
    {
        cout<<"A::f1"<<endl;
    }
    virtual void f2(void)
    {
        cout<<"A::f2"<<endl;
    }
};
class B
{
private:
    int x;
    long long xy;
    int y;

public:
    virtual void f1(void)
    {
        cout<<"B::f1"<<endl;
    }
    virtual void f2(void)
    {
        cout<<"B::f2"<<endl;
    }
};
class C
{
private:
    int x;
    long long xy;
    int y;

public:
    virtual void f1(void)
    {
        cout<<"C::f1"<<endl;
    }
    virtual void f2(void)
    {
        cout<<"C::f2"<<endl;
    }
};
class D
{
private:
    int x;
    long long xy;
    int y;

public:
    virtual void f1(void)
    {
        cout<<"D::f1"<<endl;
    }
    virtual void f2(void)
    {
        cout<<"D::f2"<<endl;
    }
};

void normal_func1(){};
void normal_func2(){};
void normal_func3(){};


int global_int = 0;
int global_uni;

int main()
{
    static int static_int = 0;

    cout<<"(long*)normal_func1   "<<(long*)(normal_func1)<<endl;
    cout<<"(long*)normal_func2   "<<(long*)(normal_func2)<<endl;
    cout<<"(long*)normal_func3   "<<(long*)(normal_func3)<<endl;

    cout<<"(long*)(&global_int)  "<<(long*)(&global_int)<<endl;
    cout<<"(long*)(&static_int)  "<<(long*)(&static_int)<<endl;
    cout<<"(long*)(&global_uni)  "<<(long*)(&global_uni)<<endl;
    cout<<"global_uni            "<<         global_uni<<endl;

    B virtual_instance1;
    A virtual_instance0;
    D virtual_instance3;

    cout<<endl<<endl<<"虚函数相关: "<<endl<<endl;

    cout << "(long*)(&virtual_instance0)             " << (long*)(&virtual_instance0) << endl;
    cout << "(long*)(&virtual_instance1)             " << (long*)(&virtual_instance1) << endl;
    cout << "(long*)(&virtual_instance3)             " << (long*)(&virtual_instance3) << endl;
    cout << "(long*)*(long*)(&virtual_instance0)     " << (long*)*(long*)(&virtual_instance0) << endl;
    cout << "(long*)*(long*)(&virtual_instance1)     " << (long*)*(long*)(&virtual_instance1) << endl;
    cout << "(long*)*(long*)(&virtual_instance3)     " << (long*)*(long*)(&virtual_instance3) << endl;
    cout << "*(long*)*(long*)(&virtual_instance0)    " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance0) << endl;
    cout << "*(long*)*(long*)(&virtual_instance1)    " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance1) << endl;
    cout << "*(long*)*(long*)(&virtual_instance3)    " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance3) << endl;

    A virtual_instance00;
    int x = 0;
    cin>>x;
    if(x)
    {
        C *pC = new C();
//        C *pC = (C*)new D();
    }
    cout << "(long*)(&virtual_instance00)            " << (long*)(&virtual_instance00) << endl;
    cout << "(long*)*(long*)(&virtual_instance00)    " << (long*)*(long*)(&virtual_instance00) << endl;
    cout << "*(long*)*(long*)(&virtual_instance00)   " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance00) << endl;

    cout << "(long*)(&virtual_instance0)             " << (long*)(&virtual_instance0) << endl;
    cout << "(long*)(&virtual_instance1)             " << (long*)(&virtual_instance1) << endl;
    cout << "(long*)(&virtual_instance3)             " << (long*)(&virtual_instance3) << endl;
    cout << "(long*)*(long*)(&virtual_instance0)     " << (long*)*(long*)(&virtual_instance0) << endl;
    cout << "(long*)*(long*)(&virtual_instance1)     " << (long*)*(long*)(&virtual_instance1) << endl;
    cout << "(long*)*(long*)(&virtual_instance3)     " << (long*)*(long*)(&virtual_instance3) << endl;
    cout << "*(long*)*(long*)(&virtual_instance0)    " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance0) << endl;
    cout << "*(long*)*(long*)(&virtual_instance1)    " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance1) << endl;
    cout << "*(long*)*(long*)(&virtual_instance3)    " << hex<<"0x"<<*(long*)*(long*)(&virtual_instance3) << endl;

    typedef void(*Fun)(void);
    Fun pFunc1, pFunc2, pFunc3;
    pFunc1 = (Fun)*((long*)*(long*)(&virtual_instance0));
    pFunc1();
    pFunc2 = (Fun)*((long*)*(long*)(&virtual_instance1) + 1);
    pFunc2();
    pFunc3 = (Fun)*((long*)*(long*)(&virtual_instance3) + 1);
    pFunc3();
    return 0;
}

输出:

(long*)normal_func1   0x55e10f66bcfa
(long*)normal_func2   0x55e10f66bd01
(long*)normal_func3   0x55e10f66bd08
(long*)(&global_int)  0x55e10f86e25c
(long*)(&static_int)  0x55e10f86e268
(long*)(&global_uni)  0x55e10f86e260
global_uni            0


虚函数相关: 

(long*)(&virtual_instance0)             0x7ffcaa24d2e0
(long*)(&virtual_instance1)             0x7ffcaa24d2c0
(long*)(&virtual_instance3)             0x7ffcaa24d300
(long*)*(long*)(&virtual_instance0)     0x55e10f86dd18
(long*)*(long*)(&virtual_instance1)     0x55e10f86dcf8
(long*)*(long*)(&virtual_instance3)     0x55e10f86dcd8
*(long*)*(long*)(&virtual_instance0)    0x55e10f66c782
*(long*)*(long*)(&virtual_instance1)    0x55e10f66c7f2
*(long*)*(long*)(&virtual_instance3)    0x55e10f66c862

等待cin>>阻塞中。
整理:
在这里插入图片描述
可以看到,A::f1()-B::f1()-D::f1()之间的地址差,有个两倍的差距。如果将代码中的

  if(x)
  {
        C *pC = new C();
//        C *pC = (C*)new D();
  }

C *pC = new C(); 删除,用下面一句,则A::f1()-B::f1()-D::f1()之间的地址差是一致的。

结论:
1 . 根据代码中是否出现实例化代码(不管有没有运行到这个地方),决定是否生成虚函数。
2 . 虚表的生成顺序与类虚函数的生成顺序正好相反,猜测是编译器使用类似栈的结构,每次生成类的虚函数都会入栈(应该是按类定义的顺序?)。
3 . 如果没有出现实例化的代码,即使有类似"C* pC;"的代码,也不会生成虚函数与虚表。
4 . 无法修改虚表,若强行修改会出现段错误。

新的问题:
1 . 全局变量、局部static变量的出现顺序似乎只与其定义的顺序有关。且未初始化的全局变量、局部static变量似乎也是与其放在一块,并没有分开放置,是因为直接生成的可执行文件吗(链接库就会分开以节省空间?)?还是说有不同的实现?
2 . Ubuntu下64位的情况下,虚表是在全局区之前、虚函数之后,虚函数是在普通函数之后;但是在windows下其地址反而是最高的,堆空间的地址好像也没有什么规律。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
虚函数指针实际上是一个指向虚函数的指针,该指针存储在每个多态类对象的内存布局中。虚函数是一个数组,其中存储了虚函数的地址,它是在编译时由编译器生成的。虚函数的第一个元素是指向类所继承的虚基类的虚函数指针,如果没有继承虚基类,则第一个元素是指向类自身的类型信息的指针。 下面是一个简单的示例代码,演示了如何模拟虚函数指针的实现: ```c++ #include <iostream> class A { public: virtual void func1() {} virtual void func2() {} private: int m_data; }; int main() { A a; void** vptr = *(void***)(&a); std::cout << "Size of A: " << sizeof(A) << std::endl; std::cout << "Address of vptr: " << vptr << std::endl; std::cout << "Address of func1: " << vptr[0] << std::endl; std::cout << "Address of func2: " << vptr[1] << std::endl; return 0; } ``` 在上面的代码中,我们创建了一个A类型的对象a,并获取了其虚函数指针vptr。为了获取vptr,我们使用了一个指向void*类型的指针的指针,它指向a对象的地址,并将其转换为一个void**类型的指针,即指向指针的指针。然后我们使用*vptr来获取虚函数的内容,其中vptr[0]是第一个虚函数的地址,即func1()的地址,vptr[1]是第二个虚函数的地址,即func2()的地址。 注意,虚函数指针是在编译时由编译器生成的,因此我们无法在运行时修改它的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值