C++知识积累:继承关系(含虚函数)下类的内存布局

目录1 无继承2 一般继承2.1 单继承2.2 多层继承2.3 多重继承2.4菱形继承3 虚继承3.1 虚继承的布局3.2 虚继承的多层继承3.3 虚继承的菱形继承总结1 无继承 类A定义如下:class A{private: int A_1;public: static int A_2; int A_3; ...
摘要由CSDN通过智能技术生成

目录

1 无继承

2 一般继承

2.1 单继承

2.2 多层继承

2.3  多重继承

2.4 菱形继承

3 虚继承

3.1 虚继承的布局

3.2  虚继承的多层继承

3.3  虚继承的菱形继承

总结


1 无继承

      类A定义如下:

class A
{
private:
	int A_1;
public:
	static int A_2;
	int A_3;
	virtual void Vfun1() {};
	virtual void Vfun2() {};
	virtual void Vfun3() {};

};

int A::A_2 = 5;

      这里给A定义了一个私有变量,一个静态变量、一个公有变量以及3个虚函数,现在来看看A的内存布局:

      A的内存布局如下:

      从A的内存布局中可以知道:在无继承的情况下,对象内存布局中会先放置虚表指针vfptr,然后再按声明顺序存放数据成员。并且可以知道:虚表vftable中按声明先后顺序存放了A中定义的3个虚函数的地址,此外,类中定义的静态变量A_2并不存在于类中,并且类成员变量的访问权限也不会影响到成员的内存布局(比如这里的私有变量A_1和公有变量A_3是放在一起的)

 

2 一般继承

2.1 单继承

    现在定义一个A的继承类B,类B定义如下:

class B :public A
{
public:
	int B_1;
        int B_2;
	virtual void Vfun1() {};  //重写了A中定义的一个虚函数
	virtual void Vfun4() {};  //定义了一个新的虚函数
};

       在类B中,重写了父类A的虚函数Vfun1,并且定义了一个新的虚函数Vfun4,现在来看看B的内存布局:

       B的内存布局如下:

 

       从这里可以看到,在类B中的前面,存放了类A的整个布局,即类A中的所有非静态数据成员都原封不动地放在了类B的前面,然后才是类B自己的数据成员B_1和B_2。不过值得注意的是,在类B的最前面,存放的依然是一个虚表指针vfptr,再来看vfptr所指的虚表中,由于类B重写了虚函数Vfun1,因此B中定义的虚函数的地址&B::Vfun1覆盖了之前的&A::Vfun1,并且在虚表最后还加上了类B中新定义的虚函数地址&B::Vfun4。

       因此我们可以得出以下结论:在子类的内存布局的最前面存放的是父类的内存布局,一开始依然是存放一个虚表指针,只不过这个虚表指针与父类的虚表指针是不一样,这个虚表指针所指向的虚函数表中,会包含所有父类中的虚函数(不一定是父类所定义的,也可能是父类从爷爷类继承下来的虚函数)的地址以及子类中新定义的虚函数的地址,如果子类对虚函数进行了重写,那么重写后的虚函数地址将会覆盖相应的父类虚函数地址。这也说明了如果父类和子类中有不同的虚函数,最终在子类中也只有一个虚表指针,对应一个虚函数表

       那么要是多层继承也是这样吗?

2.2 多层继承

       现在来定义一个类C,用于继承类B,类C的定义如下:

class C :public B
{
	int C_1;
	int C_2;
	virtual void Vfun1() {};   //重写了B中定义的虚函数(这个函数在B中也是重写的A定义的虚函数)
	virtual void Vfun2() {};   //重写了A中定义的虚函数(这个函数在B中未被重写)
	virtual void Vfun4() {};   //重写了B中新定义的虚函数(这个函数未在A中定义)
	virtual void Vfun5() {};   //定义了一个新的虚函数
};

       现在先不看C的内存布局结果,试着来推测一下:

       首先,如前所述:子类的内存布局最前面存放的是父类的内存布局,因此在类C的内存布局中,最开始应当存放一个类B的布局,相当于直接把上面B的内存布局搬到C中,最前面依然是一个虚表指针vfptr,只不过这个虚表指针与原先B中的虚表指针完全不一样。接着在B的布局之后,则应当是按照声明顺序存放的C中的成员变量,如下所示:

        再来看虚表中的内容:根据前面所分析的,如果C中没有进行任何的虚函数的声明定义,那么C中虚表指针所指虚表中应当与B中虚表指针所指虚表的内容一样了。而现在类C重写了Vfun1,Vfun2和Vfun4,并且新定义了一个虚函数Vfun5,那么虚表中的内容应当如下所示:(黑色表示未发生重写的父类虚函数,蓝色表示重写的虚函数,紫色表示新定义的虚函数)

          有了上述的分析,再来看下类C实际的内存布局:

         由此可见,完全符合前面的分析结果。

         为了考虑更多的情况,下面再来分析一下多重继承的情况。

2.3  多重继承

         多重继承的是指一个子类含多个不同的父类。为了验证这种情况,将B与A的继承关系取消,让C同时继承自A和B,如下所示:

class A
{
private:
	int A_1;
public:
	static int A_2;
	int A_3;
	virtual void Vfun1() {};
	virtual void Vfun2() {};
	virtual void Vfun3() {};

};
int A::A_2 = 5;
class B
{
public:
	int B_1;
	int B_2;
	virtual void Vfun1() {};
	virtual void Vfun4() {};
};
class C :public A,public B
{
public:
	int C
  • 14
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值