成员函数在内存中的分布

Posted 走过_冬天

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了成员函数在内存中的分布相关的知识,希望对你有一定的参考价值。

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

#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)

以上是关于成员函数在内存中的分布的主要内容,如果未能解决你的问题,请参考以下文章

关于虚函数,类的内存分布以及类的成员函数调用原理

指向不同类的成员函数的指针

不同类的成员函数指针的C++映射

什么类型可以保存 C++ 中不同类的成员函数指针?

如何从已在其中创建的不同类对象中访问类对象的成员函数?

虚函数和虚拟继承的内存分布