成员函数在内存中的分布

要看成员函数在内存中的分布,那需要先获得成员函数的地址,于是可以用函数指针指向成员函数,再打印地址,先来看下面一段代码:

#include <iostream>
#include <stdio.h>

using namespace std;

class ClassA
{
public:
    ClassA(){m_data1 = 1; m_data2 = 2;}
    int m_data1;
    int m_data2;

    void func1(){cout << "ClassA::func1" << endl;}
    void func2(){cout << "ClassA::func2" << endl;}
    virtual void vfunc1(){cout << "ClassA::vfunc1" << endl;}
    virtual void vfunc2(){cout << "ClassA::vfunc2" << endl;}
};

class ClassB : public ClassA
{
public:
    ClassB(){m_data3 = 3;}
    int m_data3;
    void func2(){cout << "ClassB::func2" << endl;}
    virtual void vfunc1(){cout << "ClassB::vfunc1" << endl;}
};

class ClassC : public ClassB
{
public:
    ClassC(){m_data4= 5; m_data1 = 4;}
    int m_data1;
    int m_data4;
    void func2(){cout << "ClassC::func2" << endl;}
    virtual void vfunc1(){cout << "ClassC::vfunc1" << endl;}
};

int main()
{
    ClassA a;
    ClassB b;
    ClassC c;
	
	//函数返回值类型 (* 指针变量名) (函数参数列表);
    //返回类型(类名::*指针名)(参数)=&类名::函数名;
    void (ClassA::*pmfa)();

    pmfa = &ClassA::func1;
    printf("ClassA::func1=%#x  ", pmfa);
    (a.*pmfa)();

    pmfa = &ClassA::func2;
    printf("ClassA::func2=%#x  ", pmfa);
    (a.*pmfa)();

    pmfa = &ClassA::vfunc1;
    printf("ClassA::vfunc1=%#x ", pmfa);
    (a.*pmfa)();

    pmfa = &ClassA::vfunc2;
    printf("ClassA::vfunc2=%#x ", pmfa);
    (a.*pmfa)(); printf("\n");


    void (ClassB::*pmfb)();
    pmfb = &ClassB::func1;
    printf("ClassB::func1=%#x  ", pmfb);
    (b.*pmfb)();

    pmfb = &ClassB::func2;
    printf("ClassB::func2=%#x  ", pmfb);
    (b.*pmfb)();

    pmfb = &ClassB::vfunc1;
    printf("ClassB::vfunc1=%#x ", pmfb);
    (b.*pmfb)();

    pmfb = &ClassB::vfunc2;
    printf("ClassB::vfunc2=%#x ", pmfb);
    (b.*pmfb)();    printf("\n");


    void (ClassC::*pmfc)();
    pmfc = &ClassC::func1;
    printf("ClassC::func1=%#x  ", pmfc);
    (c.*pmfc)();

    pmfc = &ClassC::func2;
    printf("ClassC::func1=%#x  ", pmfc);
    (c.*pmfc)();

    pmfc = &ClassC::vfunc1;
    printf("ClassC::vfunc1=%#x ", pmfc);
    (c.*pmfc)();

    pmfc = &ClassC::vfunc2;
    printf("ClassC::vfunc2=%#x ", pmfc);
    (c.*pmfc)(); printf("\n");
    
    return 0;
}

运行结果:

ClassA::func1=0x565bf198  ClassA::func1
ClassA::func2=0x565bf1de  ClassA::func2		
ClassA::vfunc1=0x1 ClassA::vfunc1
ClassA::vfunc2=0x5 ClassA::vfunc2

ClassB::func1=0x565bf198  ClassA::func1
ClassB::func2=0x565bf2ec  ClassB::func2
ClassB::vfunc1=0x1 ClassB::vfunc1
ClassB::vfunc2=0x5 ClassA::vfunc2

ClassC::func1=0x565bf198  ClassA::func1
ClassC::func1=0x565bf3be  ClassC::func2
ClassC::vfunc1=0x1 ClassC::vfunc1
ClassC::vfunc2=0x5 ClassA::vfunc2

从运行结果可以看出:

  1. 对于类的普通函数(ClassA::func1),子类在继承后并不会复制函数.
  2. 而当子类override父类的同名函数后,子类函数(ClassA::func2)有新的函数地址.
  3. vfunc1就有点奇怪了,子类明明override了,怎么不同类的vfunc1函数地址相同呢? 如果函数地址相同,那多态是怎么实现的,更奇怪的是从输出结果来看,确实实现了多态调用?

我们知道对于含有虚函数的类对象,编译器会为类生成一个是虚函数表,如果vfunc1,vfunc2指向的是虚函数表,再通过查找虚函数表选出对应函数,好像还能解释得通目前的现象(虽然(0x01, 0x05)这两个地址看着就有点不像一个正常地址),于是我打印了出虚函数表的地址

#include <iostream>
#include <stdio.h>

using namespace std;

class ClassA
{
public:
    ClassA(){m_data1 = 1; m_data2 = 2;}
    int m_data1;
    int m_data2;

    void func1(){cout << "ClassA::func1" << endl;}
    void func2(){cout << "ClassA::func2" << endl;}
    virtual void vfunc1(){cout << "ClassA::vfunc1" << endl;}
    virtual void vfunc2(){cout << "ClassA::vfunc2" << endl;}
};

class ClassB : public ClassA
{
public:
    ClassB(){m_data3 = 3;}
    int m_data3;
    void func2(){cout << "ClassB::func2" << endl;}
    virtual void vfunc1(){cout << "ClassB::vfunc1" << endl;}
};

class ClassC : public ClassB
{
public:
    ClassC(){m_data4= 5; m_data1 = 4;}
    int m_data1;
    int m_data4;
    void func2(){cout << "ClassC::func2" << endl;}
    virtual void vfunc1(){cout << "ClassC::vfunc1" << endl;}
};

int main()
{
    ClassA a;
    ClassB b;
    ClassC c;

	long *v;    // v is the address of vptr
    long *vtbl0;    // vtbl0 is the address of vtbl[0]
    
	v = (long*)&a;
    vtbl0 = (long*)(*v);
    cout << "ClassA.vptr" << v << " ClassA.vtbl[0]" << vtbl0 << endl;

	v = (long*)&b;
    vtbl0 = (long*)(*v);
    cout << "ClassB.vptr" << v << " ClassB.vtbl[0]" << vtbl0 << endl;

    v = (long*)&c;
    vtbl0 = (long*)(*v);
    cout << "ClassC.vptr" << v << " ClassC.vtbl[0]" << vtbl0 << endl;

	return 0;    
}
    

运行结果:

ClassA.vptr=0xfffd6d78 ClassA.vtbl[0]=0x56650e88
ClassB.vptr=0xfffd6d84 ClassB.vtbl[0]=0x56650e78
ClassC.vptr=0xfffd6d94 ClassC.vtbl[0]=0x56650e68

向地址中可以看出不同类有自己的虚函数表,所以vfunc1,vfunc2显然不是指向类的虚函数表

这里涉及到从虚函数调用出发到查找虚函数表这个过程编译器是如何实现的.就像一个黑盒子,我们知道进去的是什么,也知道输出是什么,却不知道中间的处理过程是什么样的.有知道的朋友,欢迎留言,我的邮箱地址(zhangxu2005419@163.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值