【温故而知新】C++中类的大小与其继承关系
来源:互联网 发布:苏打饼干 知乎 编辑:程序博客网 时间:2024/06/06 16:03
【本文参考微信公众号:“程序猿”,账号:imkuqin,原文链接】
1、空类:
//NullClass.h#pragma onceclass CNullClass{};
查看CNullClass实例的大小:
int _tmain(int argc, _TCHAR* argv[]){CNullClass *pNullClass;pNullClass = new CNullClass;printf("size of CNullClass:%d\n",sizeof(*pNullClass));delete pNullClass;return 0;}
输出结果显示,CNullClass实例的大小为1,即空类的实例只占一个字节,且该字节的内容并无实质意义。
2、只包含方法成员的类
//SimpleClass.h#pragma onceclass CSimpleClass{public:CSimpleClass();~CSimpleClass();void memthod1();};//SimpleClass.cpp#include "stdafx.h"#include "SimpleClass.h"CSimpleClass::CSimpleClass(){printf("CSimpleClass constructed.\n");}CSimpleClass::~CSimpleClass(){printf("CSimpleClass destructed.\n");}void CSimpleClass::memthod1(){printf("memthod1 called.\n");}//main.cpp#include "stdafx.h"int _tmain(int argc, _TCHAR* argv[]){CSimpleClass *pSimpleClass;pSimpleClass = new CSimpleClass;printf("size of <span style="font-family: 'Microsoft YaHei';">CSimpleClass </span>:%d\n", sizeof(*pSimpleClass));delete pSimpleClass;return 0;}
运行结果:
由此可知,一个类的成员函数并不影响类的实际大小。
3、包含数据成员的类
class COneMemberClass{public:COneMemberClass(int data = 0){m_nData = data;m_nData2 = 0;}~COneMemberClass(){};void foo();void setData2(int data2);private:int m_nData;int m_nData2;};void COneMemberClass::foo(){printf("data: %d.\ndata2: %d.\n",m_nData,m_nData2);}void COneMemberClass::setData2(int data2){m_nData2 = data2;}int _tmain(int argc, _TCHAR* argv[]){COneMemberClass OneMemClass(1);OneMemClass.setData2(5);OneMemClass.foo();printf("Size of COneMemberClass is %d.\n", sizeof(OneMemClass));return 0;}
运行结果:
内存结构如下:
可见,这种情况下,对象的保存内容为数据成员的值,按照声明中的顺序。
4、简单继承类:
class CSonClass :public COneMemberClass{public:CSonClass();~CSonClass();void foo();private:int m_nSonData;};CSonClass::CSonClass(){m_nSonData = 3;}CSonClass::~CSonClass(){}void CSonClass::foo(){printf("Son Class member - data:%d\n",m_nSonData);}int _tmain(int argc, _TCHAR* argv[]){CSonClass sonObj;sonObj.setData2(2);sonObj.foo();return 0;}
运行结果:
Son Class member - data : 3内存结构为:
在内存中,首先存放的是基类的成员,然后是派生类的成员。如果派生类继续向下派生,则其子类的数据继续存放在后续地址中。
5、虚继承的类:
#include "OneMemberClass.h"class VirtualInheritClass : virtual public COneMemberClass{private:int nData;};
运行结果:
内存结构:
可见,在虚继承的情况下,类的最前面多了8个字节的数据,这些字节中保存了一个指针变量,指向一个关于虚基类偏移量的数组,偏移量是关于虚基类数据成员的偏移量。
另外对于某一个类继承自多个父类(B和C),这些父类又虚拟继承自一个共同的父类(A)的情况,内存结构如:(偏移量指针)(B的数据成员)(偏移量指针)(C的数据成员)(本对象的数据成员)(基类A的数据成员)。虚拟继承利用虚基类偏移量表指针,使得即使某各类被重复继承,基类的成员也只存在一次。
6、带有虚函数的情况
第一种,一个空类中声明了一个虚函数:
class CVirtualNull{public:CVirtualNull();~CVirtualNull();virtual void foo();};CVirtualNull::CVirtualNull(){printf("VirtualNull Class constructed.\n");}CVirtualNull::~CVirtualNull(){}void CVirtualNull::foo(){printf("Foo.\n");}
在这种情况下,CVirtualNull对象的大小为4,其内容只有一个指针,指向该类的虚函数表。
第二种,某个子类继承了一个声明了虚函数的父类:
class CSubVirtualClass :public CVirtualNull{public:CSubVirtualClass();~CSubVirtualClass();private:int m_nubVirtualNull;};CSubVirtualClass::CSubVirtualClass(){m_nubVirtualNull = 5;}CSubVirtualClass::~CSubVirtualClass(){}
此时,CSubVirtualClass对象的大小为8字节。CSubVirtualClass类虽然没有直接声明虚函数,但是依然包含了基类的虚函数表(地址不同,指向内存的内容)。内存结构如下:
在单继承情况下,当子类自己定义了虚函数时,依然只有一个虚函数表(继承自父类),子类的虚函数指针将添加到这个虚函数表的后面。但当多继承时,子类中可能包含多个虚函数表,分别对应每一个父类。
第三种,包含虚函数类的虚继承
class VirtualInheritance{private:int m_men1;public:virtual void aa(){};};class sonClass1 : public virtual VirtualInheritance{private:int m_mem2;public:virtual void bb(){};};class sonClass2 : public virtual sonClass1{private:int m_mem3;public:virtual void cc(){};};
在 visual studio中的输出结果为:
对于VirtualInheritance类,其包含一个虚函数表+自身成员,大小为8。
对于sonClass1类,由于是虚拟继承因此包含一个虚函数偏移指针;由于自身存在虚函数,因此包含一个指向虚函数表指针;随后是自身的数据成员,最后是完整的父类对象。sonClass2情况与此类似。
总结:
1,普通单继承,只需将自身成员变量的大小加上父类大小(父类中 有虚函数,子类中不管有没有)若父类没有虚函数,则子类大小需要加上指向虚表的指针大小。
2,普通多继承,若几个父类都有虚表,则子类与第一个父类公用一个虚表指针,其他有几个有虚函数的父类则就有几个虚表指针。
3,虚拟单继承,此时若子类有虚函数则加上一个自身的虚表指针的大小,(若没有则不加)再加上自身的成员变量大小,还要加上一个虚类指针ptr_sonclass_fatherclass,最后加上父类的大小。
4,多重虚拟继承,此时若子类有虚函数则加上一个自身的虚表指针的大小,(若没有则不叫)再加上自身的成员变量大小,还要加上 一个公用的虚类指针(不管有几个虚拟父类,只加一个),在加上所有父类的大小。
5、普通、虚拟混合多继承,此时子类的大小为自身大小(若子类或普通父类有虚函数,则为成员变量+虚表指针大小;若都没虚函数,则就为成员变量大小),加上一个虚类指针大小,在加上虚拟父类的大小,在加上普通父类的大小(除虚表指针,因为它和子类公用一个虚表指针)。
- 【温故而知新】C++中类的大小与其继承关系
- C++虚继承与其他继承类的sizeof()大小详解
- C++虚继承与其他继承类的sizeof()大小详解
- 虚拟继承和多重继承中类对象的大小
- PHP中类的继承关系
- Unity中类的关系继承图
- 继承类的大小
- Objective-C 类继承关系
- C#中类的继承
- C++(虚)继承类的内存占用大小
- C++(虚)继承类的内存占用大小
- 视频大小与其参数之间的关系及视频转码注意事项
- 温故而知新,再次学习hibernate的配置关系
- C++-继承:基类与派生类的关系
- .NET(C#):判断Type类的继承关系
- C++:C++ 空类的大小及相关继承类的大小
- Java的子类构造器与其父类关系
- 浅谈java的子类构造器与其父类关系
- 第五周【项目1-体验常成员函数】
- 坦克大战-引擎架构
- 探究redis和memcached的 LRU算法--------redis的LRU的实现
- Git学习笔记(五) Git重置
- 机器学习基石笔记14——机器可以怎样学得更好(2)
- 【温故而知新】C++中类的大小与其继承关系
- JAVA内存结构之运行时栈帧结构
- JAVA里面GridLayout的作用
- 37. PHP 字符串运算符
- 自定义View之onMeasure()
- hdu 1421 搬寝室 dp 类似背包
- 2015-第5周项目1-体验常成员函数
- Git学习笔记(六) Git检出
- 第四届acm F题(Alice and Bob)(巧用二进制)