class类的大小计算【空类,虚函数类,继承】

1、类的大小

首先必须先了解class的对齐问题,class和struct的对齐规则一样 :struct的用法和struct的对齐原则
这里我们所说的类的大小,也就是类的空间占用,所以类的大小和该类对象的大小始终都是一致的。

那么哪些因素可以影响类的大小,哪些是无关紧要的因素呢?

有关的因素:

  • 普通成员变量:在实例对象中存在,需要注意对齐问题
  • 虚函数:有虚函数的类就会有虚函数表,类的每个实例对象都有一个虚函数指针vptr指向虚函数表。
  • 继承:子类会继承父类的成员变量和函数(考虑虚函数)。

无关的因素

  • 静态成员变量和静态成员函数:占用全局的内存,类似全局变量,class 和 static 组合的时候, class 就只是一个 namespace 的作用了.
  • 普通成员函数:类的普通成员函数是该类所有实例共享的(你可以把它看做普通的全局函数),只是在调用时通过隐藏的this指针和类的实例相关联,函数代码编译后根本就不在类实例中,所以不占实例空间。

 

2、空类

空类即什么都没有的类,按上面的说法,照理说大小应该是0,但是,空类的大小为1,因为空类可以实例化,实例化必然在内存中占有一个位置,因此,编译器为其分配1个字节大小。

子类继承空类:

class base{

};

class son:public base{
 private:
    int a;
}

base为空类,大小为1字节;子类son的大小为4字节,也就是其成员变量int的大小。
那么子类son为什么没有加上父类base的大小?是因为空白基类优化(在空基类被继承后,子类会优化掉基类的1字节的大小,节省空间,提高运行效率。)

 

3、含虚函数的单一继承

如下代码:

class Base{
	private:
	    char a;
	public:
	    virtual void f();
	    virtual void g();
};

class Derived:public Base{
	private:
	    int b;
	public:
	    void f();
};

class Derived1:public Base{
	private:
	    double b;
	public:
	    void g();
	    virtual void h();
};

基类Base中含有一个char型成员变量,以及两个虚函数,此时Base类的内存布局如下:

内存布局的最一开始是vfptr(virtual function ptr)即虚函数表指针(只要含虚函数,一定有虚函数表指针,而且该指针一定位于类内存模型最前端),接下来是Base类的成员变量,按照在类里的声明顺序排列,当然还是要注意内存对齐原则!

 

继承类Derived继承了基类,重写了Base中的虚函数f(),还添加了自己的成员变量,即int型的b,这时,Derived的类内存模型如下:

 

此种情况下,最一开始的还是虚函数表指针,只不过,在Derived类中被重写的虚函数f()在对应的虚函数表项的Base::f()已经被替换为Derived::f(),接下来是基类的成员变量char a,紧接着是继承类的成员变量int b,按照其基类变量声明顺序与继承类变量声明顺序进行排列,并注意内存对齐问题。

 

继承类Derived1继承了基类,重写了Base中的虚函数g(),还添加了自己的成员变量(即double型的b)与自己的虚函数(virtual h() ),这时,Derived1的类内存模型如下:

此种情况下,Derived1类一开始仍然是虚函数表指针,只是在Derived1类中被重写的虚函数g()在对应的虚函数表项的Base::g()已经被替换为Derived1::g(),新添加的虚函数virtual h()位于虚函数表项的后面,紧跟着基类中最后声明的虚函数表项后,接下来仍然是基类的成员变量,紧接着是继承类的成员变量。

 

4、含虚函数的多重继承

class Base1{
	private:
	    char a;
	public:
	    virtual void f();
	    virtual void g1();
};

class Base2{
	private:
	    int b;
	public:
	    virtual void f();
	    virtual void g2();
};

class Base3{
	private:
	    double c;
	public:
	    virtual void f();
	    virtual void g3();
};

class Derived:public Base1, public Base2, public Base3{
	private:
	    double d;
	public:
	    void f();
	    virtual void derived_func();
}

首先继承类多重继承了三个基类,此外继承类重写了三个基类中都有的虚函数virtual f(),还添加了自己特有的虚函数derived_func(),那么,新的继承类内存布局究竟是什么样子的呢?请看下图!先来看3个基类的内存布局:

紧接着是继承类Derived的内存布局:

首先,Derived类自己的虚函数表指针与其声明继承顺序的第一个基类Base1的虚函数表指针合并,此外,若Derived类重写了基类中同名的虚函数,则在三个虚函数表的对应项都应该予以修改,Derived中新添加的虚函数位于第一个虚函数表项后面,Derived中新添加的成员变量位于类的最后面,按其声明顺序与内存对齐原则进行排列。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值