精华内容
下载资源
问答
  • 字节对齐的原理见链接https://www.cnblogs.com/0201zcr/p/4789332.html(注意64位系统虚函数指针占8字节,32位占4字节) 计算一个对象的大小时的规律: 1、空、单一继承的空、多重继承的空...

    前半部分转自https://www.cnblogs.com/SeekHit/p/7570247.html

    其中为32位字节对齐,后半部分给出自己的理解。

    字节对齐的原理见链接https://www.cnblogs.com/0201zcr/p/4789332.html(注意64位系统虚函数指针占8字节,32位占4字节)

    计算一个类对象的大小时的规律:

    • 1、空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,下同);
    • 2、一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;
    • 3、因此一个对象的大小≥所有非静态成员大小的总和;
    • 4、当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;
    • 5、虚承继的情况:由于涉及到虚函数表和虚基表,会同时增加一个(多重虚继承下对应多个)vfPtr指针指向虚函数表vfTable和一个vbPtr指针指向虚基表vbTable,这两者总共所占的空间大小为:8(或8乘以多继承时父类的个数);理解为虚函数表和虚基表分别占4字节。即基类的虚函数表总共占4个字节,子类的虚基表每个基类地址占4个字节
    • 6、在考虑以上内容所占空间的大小时,还要注意编译器下的“补齐”padding的影响,即编译器会插入多余的字节补齐;
    • 7、类对象的大小=各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和+ vfptr指针(多继承下可能不止一个)+vbptr指针(多继承下可能不止一个)+编译器额外增加的字节。
    • 例子一

      class A     
      {     
      };    
        
      class B     
      {  
          char ch;     
          virtual void func0()  {  }   
      };   
        
      class C    
      {  
          char ch1;  
          char ch2;  
          virtual void func()  {  }    
          virtual void func1()  {  }   
      };  
        
      class D: public A, public C  
      {     
          int d;     
          virtual void func()  {  }   
          virtual void func1()  {  }  
      };     
        
      class E: public B, public C  
      {     
          int e;     
          virtual void func0()  {  }   
          virtual void func1()  {  }  
      };  
        
      int main(void)  
      {  
          cout<<"A="<<sizeof(A)<<endl;    //result=1 //64位为1
          cout<<"B="<<sizeof(B)<<endl;    //result=8 //64位为16 
          cout<<"C="<<sizeof(C)<<endl;    //result=8 //64位为16
          cout<<"D="<<sizeof(D)<<endl;    //result=12//64位为24
          cout<<"E="<<sizeof(E)<<endl;    //result=20//64位为40
          return 0;  
      }  

    原帖中写到“求sizeof(D)的时候,需要明白,首先VPTR指向的虚函数表中保存的是类D中的两个虚函数的地址(只有一个虚基类地址指针),然后存放基类C中的两个数据成员ch1、ch2,注意内存对齐,然后存放数据成员d,这样4+4+4=12。”32位系统虚函数表指针内存是4字节,而64位系统虚函数表指针内存是8字节。所以在64位系统中结果不同于32位系统,过程为8+2+4对齐为8+8+4(8+8是满足父类的对齐要求)继续对齐为8+8+8满足(对齐为最大类型内存整数倍的对齐要求),故结果为24.
    32位系统中求sizeof(E)的时候,首先是类B的虚函数地址,然后类B中的数据成员,再然后是类C的虚函数地址,然后类C中的数据成员,最后是类E中的数据成员e,同样注意内存对齐,这样4+4+4+4+4=20。

    64位系统中求sizeof(E)的时候,首先是类B的虚函数地址(8字节),然后类B中的数据成员(并对齐为8+8=16字节),再然后是类C的虚函数地址(8字节),然后类C中的数据成员(8+1+1对齐为8+8=16字节),最后是类E中的数据成员e,同样注意内存对齐(8字节),这样16+16+8=40。

    展开全文
  • VS2010命令行查看虚函数表内存布局 ...学习多重继承下的Virtual functions时,需要分析派生虚函数表(vtable),但是网上找了好几种Hack vtable方法,结果都不尽如人意。没想到MS Compiler(以VS2

    一 VS2010命令行查看虚函数表和类内存布局


    以下内容引自<http://blog.csdn.net/daydreamingboy/article/details/8982563>


    VS2010命令行下查看虚函数表和类内存布局

    ——《深度探索C++对象模型》读书札记系列

     

    在学习多重继承下的Virtual functions时,需要分析派生类的虚函数表(vtable),但是在网上找了好几种Hack vtable方法,结果都不尽如人意。没想到MS Compiler(以VS2010为例)有打印vtable的编译选项,真是太好了!

    1. 打开“Visual Studio Command Prompt (2010)”,如下


    该CMD下具有VS2010命令行下的一些编译、链接等工具,例如cl.exe。

     

    2. 编写一个cpp文件

    以《深度探索C++对象模型》的160页的代码(160.cpp)为例,如下

    class Base1 {
    public:
    	Base1();
    	virtual ~Base1();
    	virtual void speackClearly();
    	virtual Base1* clone() const;
    protected:
    	float data_Base1;
    };
    
    class Base2 {
    public:
    	Base2();
    	virtual ~Base2();
    	virtual void mumble();
    	virtual Base2* clone() const;
    protected:
    	float data_Base2;
    };
    
    class Derived : public Base1, public Base2 {
    public:
    	Derived();
    	virtual ~Derived();
    	virtual Derived* clone() const;
    protected:
    	float data_Derived;
    };
    
    int main(void)
    {
    	return 0;
    }

    3、使用cl命令的/d1 reportAllClassLayout或reportSingleClassLayoutXXX选项。这里的reportAllClassLayout选项会打印大量相关类的信息,一般用处不大。而reportSingleClassLayoutXXX选项的XXX代表要编译的代码中类的名字(这里XXX类),打印XXX类的内存布局和虚函数表(如果代码中没有对应的类,则选项无效)。

    举例如下

    cl /d1 reportSingleClassLayoutBase1 160.cpp

    运行结果下


    可以看出Base1的大小为8个字节,共有3个虚函数,分别是~Base1、speackClearly和clone,对于学习上述的示例代码绰绰有余咯~~


    二 具有虚函数的类的大小



    一般的书上都说,虚函数是在运行时根据对象的实际类型“动态决定”函数入口。但什么是“动态决定”呢?实际上C++编译器在实现这个功能的时候,并非真的 等到虚函数被调用时才去判断这个对象是什么类型的。下面我用一个简单的图表来说明C++编译器到底干了些什么。假设有两个类
    struct Base {
      virtual void f();
      virtual void g();
    };
    struct Derived : public Base {
      virtual void f();
      virtual void g();
    };

    Base 和 Derived 各有一个虚表,分别是 VTable_B 和 VTable_D ,那么编译器是怎么通过只用一个虚表指针来实现下面的“动态”调用呢?
    Base *pB = new Derived();
    pB->f();

    先让我们看Base和Derived对象是怎么存储的,以及两个类的虚表结构

    Base:                    VTable_B:
    ------------         -------------
    |  vptr       |        |  f() 入口    |
    +---------+        +----------+
    |  Base的   |        |  g() 入口   | 
    |    数据     |        -------------
    ------------

    Derived:                VTable_D:
    ------------        --------------
    |  vptr       |        | f() 入口     |
    +---------+        +----------+
    |  Base的   |        | g() 入口    |
    |    数据     |        -------------
    +---------+
    | Derived的|
    |    数据     |
    ------------

    pB 指针既可以指向 Base 对象,也可以指向 Derived 对象,所以 pB 本身是不确定的。但是,任何对象本身却从被 new 出来开始就是确定的,所以 Base 对象在构造时,编译器会往 vptr 中填上 VTable_B 的地址,而 Derived 对象在构造时,编译器会往 vptr 中填上 VTable_D 的地址

    等到虚函数被调用的时候,也就是 pB->f() 这行语句被执行的时候,编译器并不需要知道 pB 到底是指向 Base 还是 Derived ,它只要直接用 vptr 就能找到正确的虚表和虚函数入口了,父类和子类的虚表结构是相似的,同一个虚函数入口在父表和子表的偏移量都是一样的

    通过上面这些介绍,我想你应该能理解,为什么在单一继承的条件下,不管有多少层继承,每个对象只需一个 vptr 就行了。

    多重继承的条件下,一个 vptr 行不行呢?

    不行。多重继承的时候,虚函数既有来自父类1的,也有来自父类2的,所以这些虚函数入口是不能放在同一个虚表当中的。假设 Derived 除了 Base外,还继承 Base2,并且 Base2 中有两个虚函数 x() 和 y (),那么 Derived 对象的存储结构也许是这样的(只是大概,和具体编译器相关)。

    Derived:                VTable_D:
    ------------        --------------
    |  vptr       |        | f() 入口     |
    +---------+        +----------+
    |  Base的   |        | g() 入口    |
    |    数据     |        -------------
    +---------+
    | vptr2      |          VTable_D2:
    +---------+        -------------
    | Base2的  |        |  x() 入口    | 
    |    数据     |        +-----------+
    +---------+        | y() 入口     |
    | Derived的|        -------------
    |    数据     |

    本文版权归作者 kanego 和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

    三 案例分析1:


    如下程序:
    #include <iostream>
    using namespace std;
    
    
    
    class A
    {
    	int a;
    public:
    	virtual void af(){}
    	virtual ~A(){}//如果注释,那么class D的大小变为12,这点想不通
    };
    
    class B : public A
    {
    	int b;
    public:
    	virtual void bf(){}
    	virtual ~B(){ /*cout << "B::~B()" << endl;*/ }
    };
    
    class C : public A
    {
    	int c;
    public:
    	virtual void cf(){}
    	virtual ~C(){ /*cout << "B::~B()" << endl;*/ }
    };
    
    class D :public B, public C
    {
    	int d;
    public:
    	virtual void df(){}
    	virtual ~D() { /*cout << "D::~D()" << endl;*/ }
    };
    
    int main(void)
    {
    	cout << "A=" << sizeof(A) << endl;    //result=1
    	cout << "B=" << sizeof(B) << endl;    //result=8    
    	cout << "C=" << sizeof(C) << endl;    //result=8
    	cout << "D=" << sizeof(D) << endl;    //result=12
    	//cout << "E=" << sizeof(E) << endl;    //result=20
    
    	system("pause");
    	return 0;
    }
    

    输出结果:
    A=8
    B=12
    C=12
    D=28
    请按任意键继续. . .
    

    案例分析:
    类A有一个虚函数表占4个字节,有自己int类型占4个字节,所以sizeof(A) = 8;
    类B继承类A,属于单继承,所以类B只有一个虚函数表指针,占4个字节,继承自A的int变量,还有自身的int变量,所以sizeof(B) = 12;
    类C与类B相似;
    类D继承自B和C,有两个虚函数表指针,占据8个字节,继承自B的成员变量占8个字节,继承自C的成员变量占8个字节,自身有一个int变量占4字节,所以sizeof(D) = 28。


    四 案例分析二:

    空类所占内存大小:


    class CBase 

    }; 
    sizeof(CBase)=1;

    为什么空的什么都没有是1呢?
    c++要求每个实例在内存中都有独一无二的地址。//注意这句话!!!!!!!!!!
    空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。


    程序:
    class A
    {
    };
    class A2
    {
    };
    
    class B :public A
    {
    };
    
    class D :public A, public A2
    {
    };
    
    class C :public virtual B
    {
    };
    
    int main()
    {
    	cout << sizeof(A) << endl;
    	cout << sizeof(B) << endl;
    	cout << sizeof(C) << endl;
    	cout << sizeof(D) << endl;
    
    	system("pause");
    	return 0;
    }

    运行结果:
    1
    1
    4
    1
    请按任意键继续. . .
    

    注:B继承A存在空白基类优化现象,在空基类被继承后(单继承),由于空基类没有任何数据成员,所以让其在子类的对象布局中优化掉空基类所占用的一个字节。


    说明:空类、单一继承的空类、多重继承的空类空间为1,但是虚继承涉及到虚表(虚指针),所以sizeof(C)的大小为4

    五 案例分析三:

    class A
    {
    };
    
    class B :public A
    {
    public:
    	virtual void f1(){}
    };
    
    class C :public A
    {
    public:
    	virtual void f1(){}
    };
    
    class D :public virtual B,C
    {
    };
    
    int main()
    {
    	cout << sizeof(A) << endl;
    	cout << sizeof(B) << endl;
    	cout << sizeof(C) << endl;
    	cout << sizeof(D) << endl;
    
    	system("pause");
    	return 0;
    }

    为什么sizeof(D) =12呢?

    六 案例分析四:


    class A
    {
    };
    class B
    {
    };
    class D
    {
    };
    class E
    {
    };
    class F
    {
    };
    
    class C:public A,public B,public D,public E,public F
    {
    };
    
    class M :public A, public B
    {
    	//大小1
    };
    
    int main()
    {
    
    	cout << sizeof(C) << endl;
    	cout << sizeof(M) << endl;
    
    	system("pause");
    	return 0;
    }

    输出结果4,1为什么呢?
    多继承的情况下,为了区分各个不同的基类子对象,这些基类子对象必须具有不同的地址,所以这时候不能使用空基类优化,但单继承就可以,因为对于单继承,基类子对象与最终派生类对象地址相同的情形是允许的。

    同时要注意,空基类优化只能存在于基类子对象,当空类对象作为完整对象时,是不能优化的,因为C++规定,每个完整对象都必须具有唯一的地址。空类完整对象的大小并不是只能为1,而是至少为1,有些编译器会加入内存对齐的因素,所以有些编译器的空类完整对象的大小会是4或者8等等。

    注:若A含有静态数据成员,但是在c++里,静态成员被单独存储在全局区(静态区),所以它同样不影响A的大小。

    总结:

    class A//sizeof(A)=1
    {};
    
    class S:public A//单继承,空基类优化,sizeof(S)=4
    {
    	int a;
    };
    
    class B//B是空类
    {
    	//没有任何数据成员的类成为空类,这里的数据成员不仅仅包括类的成员变量
    	//同时还包括编译器为了某种目的引入的数据成员
    	//比如:为了支持多态而引入的虚指针vptr,为了支持虚继承而引入的必要的虚基类指针,而且还包括从
    	//基类直接或间接继承而来的上述的数据成员。
    	void fun(){}
    };
    
    class C
    {
    };
    
    class D:public A, B//双继承,空基类优化,但是只能优化一个,sizeof(D)=8
    {
    	int a;
    };
    
    class E:public A, B, C//三重继承,空基类优化一个,sizeof(E)=2
    {
    };
    
    class F:public E//单继承且E为空,空基类优化, sizeof(F)=1;
    {
    	void fun(){}
    };
    
    class G:public F//单继承,空基类优化,sizeof(G)=1
    {
    };
    
    class M{ int a; };
    class N :public M{};
    class O :public N{};
    class P :public N, A{};
    class Q :public N, A, B{};
    
    int main()
    {
    
    	cout << "A" << sizeof(A) << endl;
    	cout << "S" << sizeof(S) << endl;
    	cout << "B" << sizeof(B) << endl;
    	cout << "C" << sizeof(C) << endl;
    	cout << "D" << sizeof(D) << endl;
    	cout << "E" << sizeof(E) << endl;
    	cout << "F" << sizeof(C) << endl;
    	cout << "M" << sizeof(M) << endl;
    	cout << "N" << sizeof(N) << endl;
    	cout << "O" << sizeof(O) << endl;
    	cout << "P" << sizeof(P) << endl;
    	cout << "Q" << sizeof(Q) << endl;
    
    
    	system("pause");
    	return 0;
    }
    
    //书上说:通常C++默默安插一个char到空对象内

    运行结果:
    A1
    S4
    B1
    C1
    D8
    E2
    F1
    M4
    N4
    O4
    P4
    Q8
    请按任意键继续. . .
    


    展开全文
  • 虚函数 空间占用大小

    千次阅读 2014-09-23 10:07:16
     每一个具有虚函数的都有一个虚函数表VTABLE,里面按在类中声明的虚函数的顺序存放着虚函数的地址,这个虚函数表VTABLE是这个的所有对象所共有的,也就是说无论用户声明了多少个对象,但是这个VTABLE虚函数表...
    总结一下VPTR 和 VTABLE 和类对象的关系:
           每一个具有虚函数的类都有一个虚函数表VTABLE,里面按在类中声明的虚函数的顺序存放着虚函数的地址,这个虚函数表VTABLE是这个类的所有对象所共有的,也就是说无论用户声明了多少个类对象,但是这个VTABLE虚函数表只有一个。
           在每个具有虚函数的类的对象里面都有一个VPTR虚函数指针,这个指针指向VTABLE的首地址,每个类的对象都有这么一种指针。

    2、虚继承
         这个是比较不好理解的,对于虚继承,若派生类有自己的虚函数,则它本身需要有一个虚指针,指向自己的虚表。另外,派生类虚继承父类时,首先要通过加入一个虚指针来指向父类,因此有可能会有两个虚指针。

    二、(虚)继承类的内存占用大小
         首先,平时所声明的类只是一种类型定义,它本身是没有大小可言的。 因此,如果用sizeof运算符对一个类型名操作,那得到的是具有该类型实体的大小。
    计算一个类对象的大小时的规律:
        1、空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,下同);
        2、一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;
        3、因此一个对象的大小≥所有非静态成员大小的总和; 
        4、当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;
        5、虚承继的情况:由于涉及到虚函数表和虚基表,会同时增加一个(多重虚继承下对应多个)vfPtr指针指向虚函数表vfTable和一个vbPtr指针指向虚基表vbTable,这两者所占的空间大小为:8(或8乘以多继承时父类的个数);
        6、在考虑以上内容所占空间的大小时,还要注意编译器下的“补齐”padding的影响,即编译器会插入多余的字节补齐;
        7、类对象的大小=各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和+ vfptr指针(多继承下可能不止一个)+vbptr指针(多继承下可能不止一个)+编译器额外增加的字节。
    展开全文
  • C++的大小及虚函数表

    千次阅读 2017-04-20 23:46:16
    之前用C的时候,停挺经常计算结构体空间的,现在突然用C++了,好像还不知道C++大小怎么算的。 1....C++类中,数据成员的初始化是按照类中声明的先后顺序初始化,而与函数参数的传入顺序没有关系

    之前用C的时候,停挺经常计算结构体空间的,现在突然用C++了,好像还不知道C++类大小怎么算的。

    1. 类的组成

    我也不知道这么说是否正确,我觉得类主要有两类成员:数据成员,成员函数,并且
    * 数据成员有静态和非静态之分
    * 函数成员有静态,非静态和虚函数(virtual)之分

    数据成员
    在C++类中,数据成员的初始化是按照类中声明的先后顺序初始化,而与函数参数的传入顺序没有关系
    其中,静态函数为所有类成员所共有,不计入某个类的大小,静态成员的初始化一般在类的外部实现
    如:

    xxx.h

    class base{
      private:
          static int i;
    }

    xxx.cpp

    int base::i=1;

    注意:不要在头文件定义(初始化静态数据成员),大多数情况下会引起重复定义错误。
    成员的大小计算应该和结构体一样,下面我们来验证一下

    class basic{
        static int a;
        char b[10];
        int c;
    };
    
    int basic::a=1;
    
    
    int main ()
    {
        basic test;
        cout<<"size:"<<sizeof(test)<<endl;
        return 0;
    }

    输出为16,可以看出,a不占此类内存,b占10个字节,c四个字节,64位系统8字节对其,故为16

    * 函数成员 *
    类与结构体相比,多了属于自己的函数,类的非静态和静态函数都不占用类的内存,但是虚函数占用一个地址字节的内存。
    而且无论定义多少个虚函数,都只占用一个地址字节的内存。

    class basic{
        private:
            static int a;
            char b[10];
            int c;
    
        public:
            static void fun1(){cout<<"fun1"<<endl;}
            void fun2(){cout<<"fun2"<<endl;}
            virtual void fun3(){cout<<"fun3"<<endl;}
            virtual void fun4(){cout<<"fun4"<<endl;}
    
    
    };
    int basic::a=1;
    
    int main ()
    {
        basic test;
        cout<<"size:"<<sizeof(test)<<endl;
        return 0;
    }

    输出为24,结论正确

    既然扯到虚函数了,就说说虚函数吧
    如果一个类中存在虚函数,编译器会做以下三件事
    * 为该类分配一个虚函数表,它存有虚函数在执行器的地址
    * 在该类中安插一个虚指针,指向该类的虚表
    * 将每一个虚函数的入口地址存放在虚函数表中相应的slot

    所以在类的内存中储存的,就是指向虚函数表的虚指针
    那接下来,我们来看看,类里面的数据成员和虚函数是怎样储存的,在这里,我先输出了类成员地址,为了方便,我直接将成员替换为了公有成员

    #include <iostream>
    using namespace std;
    class basic{
        public:
            static int a;
            char b[10];
            int c;
    
            static void fun1(){cout<<"fun1"<<endl;}
            void fun2(){cout<<"fun2"<<endl;}
            virtual void fun3(){cout<<"fun3"<<endl;}
            virtual void fun4(){cout<<"fun4"<<endl;}
    };
    
    int basic::a=1;
    
    int main ()
    {
        basic test;
        cout<<"size:"<<sizeof(test)<<endl;
    
        cout<<"addr of test:"<<&test<<endl;
        cout<<"addr of test.a:"<<&test.a<<endl;
        for(int i=0;i<10;++i)
        {
            cout<<"addr of test.b["<<i<<"]:"<<(void *)&(test.b[i])<<endl;
            //这里得强制转换一下,不然坑爹的cout会把他当做字符串处理的
        }
        cout<<"addr of of test.c:"<<&test.c<<endl;
    
        return 0;
    }

    输出:

    size:24
    addr of test:0x7ffca3bd1980
    addr of test.a:0x602080
    addr of test.b[0]:0x7ffca3bd1988
    addr of test.b[1]:0x7ffca3bd1989
    addr of test.b[2]:0x7ffca3bd198a
    addr of test.b[3]:0x7ffca3bd198b
    addr of test.b[4]:0x7ffca3bd198c
    addr of test.b[5]:0x7ffca3bd198d
    addr of test.b[6]:0x7ffca3bd198e
    addr of test.b[7]:0x7ffca3bd198f
    addr of test.b[8]:0x7ffca3bd1990
    addr of test.b[9]:0x7ffca3bd1991
    addr of of test.c:0x7ffca3bd1994

    可以看出来,a的地址明显不在类的地址范围内,类的地址与第一个元素地址相差8个字节,即64位机的地址字节,这8个字节中储存虚函数指针,下面验证这一点

    #include <iostream>
    using namespace std;
    
    
    class basic{
        public:
            static int a;
            char b[10];
            int c;
    
            static void fun1(){cout<<"fun1"<<endl;}
            void fun2(){cout<<"fun2"<<endl;}
            virtual void fun3(){cout<<"fun3"<<endl;}
            virtual void fun4(){cout<<"fun4"<<endl;}
    };
    
    int basic::a=1;
    
    
    int main ()
    {
        basic test;
        cout<<"size:"<<sizeof(test)<<endl;
    
        cout<<"addr of test:"<<&test<<endl;
        cout<<"addr of test.a:"<<&test.a<<endl;
        for(int i=0;i<10;++i)
        {
            cout<<"addr of test.b["<<i<<"]:"<<(void *)&(test.b[i])<<endl;
            //这里得强制转换一下,不然坑爹的cout会把他当做字符串处理的
        }
        cout<<"addr of of test.c:"<<&test.c<<endl;
    
        void (*pfun3)(void); //函数指针
        void (*pfun4)(void); //函数指针
    
        pfun3 = (void(*)(void))*(long *)(*(long*)(&test));
        // 这里应该用typedef void(*FUN)(void)处理下会比较好,我只是想这样写写 long 刚好是64bit
        pfun3();
        pfun4 = (void(*)(void))*((long *)(*(long*)(&test))+1);
        // (long *)(&test) 将test的地址转化为系统字节长度地址再取出来  在这个地址上存放这虚函数表的地址
        // *(long *)(&test) 将虚函数指针所指向的对象取出来  得出的值应该是虚函数表的地址 同时也是第一个虚函数的地址
        // *(long *)*(long *)(&test) 从虚函数表的地址中取出内容  即第一个虚函数
        // (long *)*(long *)(&test)+1  虚函数表地址+1 即第二个虚函数地址  取出来即是fun4
        pfun4();
        return 0;
    }
    

    输出

    size:24
    addr of test:0x7ffe4ed2da70
    addr of test.a:0x602080
    addr of test.b[0]:0x7ffe4ed2da78
    addr of test.b[1]:0x7ffe4ed2da79
    addr of test.b[2]:0x7ffe4ed2da7a
    addr of test.b[3]:0x7ffe4ed2da7b
    addr of test.b[4]:0x7ffe4ed2da7c
    addr of test.b[5]:0x7ffe4ed2da7d
    addr of test.b[6]:0x7ffe4ed2da7e
    addr of test.b[7]:0x7ffe4ed2da7f
    addr of test.b[8]:0x7ffe4ed2da80
    addr of test.b[9]:0x7ffe4ed2da81
    addr of of test.c:0x7ffe4ed2da84
    fun3
    fun4

    从上面实验可以看出,类的内存结构如下图所示

    1

    fun3和fun4多写了一个括号。。。

    刚入门c++找对象,如果有出错的地方还请大神指教

    展开全文
  • 虚函数占用类空间大小(转)

    千次阅读 2017-09-27 14:16:59
    每一个具有虚函数的都有一个虚函数表VTABLE,里面按在类中声明的虚函数的顺序存放着虚函数的地址,这个虚函数表VTABLE是这个的所有对象所共有的,也就是说无论用户声明了多少个对象,但是这个VTABLE虚函数表...
  • 对象内存布局计算l 空、单一继承的空、多重继承的空所占...l 当类中声明了虚函数(不管是1个还是个),那么实例化对象时,编译器会自动对象里安插一个指针vPtr指向虚函数表VTable;l 虚承继的情况:由
  • 虚函数虚函数相当于函数指针,占用四个字节(对于32位),在类中虚函数占用四个字节,其成员函数不占的内存。基类定义虚函数,优先调用子类的同名函数,覆盖虚函数。基类指针访问不同派生对象,调用不同方法。...
  • 最近被问到一个关于继承虚函数表的问题,当时回答是可能存在虚函数表,应该是顺序排列的,但具体怎么排列还是有些疑惑的,回答的时候到有点儿心虚。之后查了资料,做了简单的实验,可以确定的是对于继承了个...
  • 虚函数表存放哪里

    千次阅读 2018-09-24 17:28:07
    3.虚函数表存储虚函数的地址,即虚函数表的元素是指向成员函数的指针,而类中虚函数的个数编译时期可以确定,即虚函数表的大小可以确定,即大小是编译时期确定的,不必动态分配内存空间存储虚函数表,所以不再堆. ...
  • 一、虚函数的工作原理  虚函数的实现要求对象携带额外的信息,这些信息用于运行时确定该对象应该...典型情况下,这一信息具有一种被称为 vptr(virtual table pointer,虚函数表指针)的指针的形式。vptr 指向一
  • 虚函数表虚函数表的指针

    千次阅读 2011-10-01 11:28:23
    有虚函数的都有一个虚函数表,它是实现多态的关键。  虚函数表可以继承,如果子类没有重写虚函数,那么子类虚函数表中仍然会有该函数的地址,只不过这个地址指向的是基类的函数实现。如果子类重写了相应的虚函数...
  • 虚函数表是一块连续的内存,每个内存单元记录一个JMP指令的地址  注意的是,编译器会为每个有虚函数的创建一个虚函数表,该虚函数表将被该的所有对象共享。的每个虚成员占据虚函数表中的一行。如果...
  • 虚函数表是一块连续的内存,每个内存单元记录一个JMP指令的地址vptr。  注意的是,编译器会为每个有虚函数的创建一个虚函数表,该虚函数表将被该的所有对象共享。的每个虚成员占据虚函数表中的一行。...
  • 虚函数占用大小Sizeof

    2014-04-30 11:14:08
    参考:... ...   根据原始定义,知道: 一旦存在虚函数,中将存在虚函数表,变量会多一个虚函数表的指针,从而sizeof时,空间会增大4字节(32位编译情况,64位编译时指针占
  • 继承、 虚继承和虚函数表的大小的影响

    千次阅读 多人点赞 2016-03-30 23:27:28
    一、真空 class CNull { }; 长度:1 内存结构: ?? 评注:长度其实为0,这个字节作为内容没有意义,可能每次都不一样。   二、空 class CNull2 { public:...
  • 静态多态和动态多态–虚函数、纯虚函数 ...2、虚函数一般用继承个子类继承同一基类,若某种行为上不同的派生有着自己的实现方式。这种情况我们就会用到多态。采用基类中将此函数定义成虚函数,派生...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 32,553
精华内容 13,021
关键字:

虚函数表在类中占用多大