C++类的大小是怎样计算的
1、方法:sizeof(className);
2、注意 求法与c的结构体类似,只算数据成员的大小,不算成员函数。
C++中数组的大小可以通过sizeof运算符来进行计算得到。
例如:对于数组V,通过sizeof(V)可以得到数组V的大小。
通过 sezeof(V)/sizeof(V[0]) 可以计算得到数组V中元素的个数。
例如:V[5] = {1,2,3,4,5};
num = sizeof(V) / sizeof(V[0]);
得到的num值就是数组V中元素的个数。
一.简单情况的计算
#include<iostream>using namespace std;
class base{ public: base()=default;
~base()=default; private: static int a; int b; char c;
};int main()
{ base obj;
cout<<sizeof(obj)<<endl;
}
计算结果:8
静态变量a不计算在对象的大小内,由于字节对齐,结果为4+4=8
二.空类的大小
本文中所说是C++的空类是指这个类不带任何数据,即类中没有非静态(non-static)数据成员变量,没有虚函数(virtual function),也没有虚基类(virtual base class)。
直观地看,空类对象不使用任何空间,因为没有任何隶属对象的数据需要存储。然而,C++标准规定,凡是一个独立的(非附属)对象都必须具有非零大小。换句话说,c++空类的大小不为0
为了验证这个结论,可以先来看测试程序的输出。
#include <iostream>using namespace std;class NoMembers
{
};int main()
{
NoMembers n; // Object of type NoMembers.
cout << "The size of an object of empty class is: "
<< sizeof(n) << endl;
}
输出:
The size of an object of empty class is: 11
C++标准指出,不允许一个对象(当然包括类对象)的大小为0,不同的对象不能具有相同的地址。这是由于:
new需要分配不同的内存地址,不能分配内存大小为0的空间
避免除以 sizeof(T)时得到除以0错误
故使用一个字节来区分空类。
但是,有两种情况值得我们注意
第一种情况,涉及到空类的继承。
当派生类继承空类后,派生类如果有自己的数据成员,而空基类的一个字节并不会加到派生类中去。例如
class Empty {};
struct D : public Empty { int a;};
sizeof(D)为4。
第二中情况,一个类包含一个空类对象数据成员。
class Empty {};class HoldsAnInt {
int x; Empty e;
};
sizeof(HoldsAnInt)为8。
因为在这种情况下,空类的1字节是会被计算进去的。而又由于字节对齐的原则,所以结果为4+4=8。
继承空类的派生类,如果派生类也为空类,大小也都为1。
三.含有虚函数成员
首先,要介绍一下虚函数的工作原理:
虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。
每当创建一个包含有虚函数的类或从包含有虚函数的类派生一个类时,编译器就会为这个类创建一个虚函数表(VTABLE)保存该类所有虚函数的地址,其实这个VTABLE的作用就是保存自己类中所有虚函数的地址,可以把VTABLE形象地看成一个函数指针数组,这个数组的每个元素存放的就是虚函数的地址。在每个带有虚函数的类
中,编译器秘密地置入一指针,称为v p o i n t e r(缩写为V P T R),指向这个对象的V TA B L E。
当构造该派生类对象时,其成员VPTR被初始化指向该派生类的VTABLE。所以可以认为VTABLE是该类的所有对象共有的,在定义该类时被初始化;而VPTR则是每个类对象都有独立一份的,且在该类对象被构造时被初始化。
假设我们有这样的一个类:
class Base {public:virtual void f() { cout << "Base::f" << endl; }virtual void g() { cout << "Base::g" << endl; }virtual void h() { cout << "Base::h" << endl; }
};1234567891011
当我们定义一个这个类的实例,Base b时,其b中成员的存放如下:
指向虚函数表的指针在对象b的最前面。
虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符”\0”一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在vs下,这个值是NULL。而在linux下,这个值是如果1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。
因为对象b中多了一个指向虚函数表的指针,而指针的sizeof是8,因此含有虚函数的类或实例最后的sizeof是实际的数据成员的sizeof加8。
例如:
class Base {public:int a;virtual void f() { cout << "Base::f" << endl; }virtual void g() { cout << "Base::g" << endl; }virtual void h() { cout << "Base::h" << endl; }
};1
sizeof(Base)为16。
vptr指针的大小为8,又因为对象中还包含一个int变量,字节对齐得8+8=16。
下面将讨论针对基类含有虚函数的继承讨论:
(1)在派生类中不对基类的虚函数进行覆盖,同时派生类中还拥有自己的虚函数,比如有如下的派生类:
class Derived: public Base
{public:virtual void f1() { cout << "Derived::f1" << endl; }virtual void g1() { cout << "Derived::g1" << endl; }virtual void h1() { cout << "Derived::h1" << endl; }
};
基类和派生类的关系如下:
当定义一个Derived的对象d后,其成员的存放如下:
可以发现:
1)虚函数按照其声明顺序放于表中。
2)基类的虚函数在派生类的虚函数前面。
此时基类和派生类的sizeof都是数据成员的大小+指针的大小8。
(2)在派生类中对基类的虚函数进行覆盖,假设有如下的派生类:
class Derived: public Base
{public:virtual void f() { cout << "Derived::f" << endl; }virtual void g1() { cout << "Derived::g1" << endl; }virtual void h1() { cout << "Derived::h1" << endl; }
};
基类和派生类之间的关系:其中基类的虚函数f在派生类中被覆盖了
当我们定义一个派生类对象d后,其d的成员存放为:
可以发现:
1)覆盖的f()函数被放到了虚表中原来基类虚函数的位置。
2)没有被覆盖的函数依旧。
派生类的大小仍是基类和派生类的非静态数据成员的大小+一个vptr指针的大小
这样,我们就可以看到对于下面这样的程序,
Base *b = new Derive();
b->f();
由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。
(3)多继承:无虚函数覆盖
假设基类和派生类之间有如下关系:
对于派生类实例中的虚函数表,是下面这个样子:
我们可以看到:
1) 每个基类都有自己的虚表。
2) 派生类的成员函数被放到了第一个基类的表中。(所谓的第一个基类是按照声明顺序来判断的)
由于每个基类都需要一个指针来指向其虚函数表,因此d的sizeof等于d的数据成员加上三个指针的大小。
(4)多重继承,含虚函数覆盖
假设,基类和派生类又如下关系:派生类中覆盖了基类的虚函数f
下面是对于派生类实例中的虚函数表的图:
我们可以看见,三个基类虚函数表中的f()的位置被替换成了派生类的函数指针。这样,我们就可以任一静态类型的基类类来指向派生类,并调用派生类的f()了。如:
Derive d;
Base1 *b1 = &d;
Base2 *b2 = &d;
Base3 *b3 = &d;
b1->f(); //Derive::f()b2->f(); //Derive::f()b3->f(); //Derive::f()b1->g(); //Base1::g()b2->g(); //Base2::g()b3->g(); //Base3::g()
此情况派生类的大小也是类的所有非静态数据成员的大小+三个指针的大小
举一个例子具体分析一下大小吧:
#include<iostream>using namespace std;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
cout<<"B="<<sizeof(B)<<endl; //result=16
cout<<"C="<<sizeof(C)<<endl; //result=16
cout<<"D="<<sizeof(D)<<endl; //result=16
cout<<"E="<<sizeof(E)<<endl; //result=32
return 0;
}
结果分析:
1.A为空类,所以大小为1
2.B的大小为char数据成员大小+vptr指针大小。由于字节对齐,大小为8+8=16
3.C的大小为两个char数据成员大小+vptr指针大小。由于字节对齐,大小为8+8=16
4.D为多继承派生类,由于D有数据成员,所以继承空类A时,空类A的大小1字节并没有计入当中,D继承C,此情况D只需要一个vptr指针,所以大小为数据成员加一个指针大小。由于字节对齐,大小为8+8=16
5.E为多继承派生类,此情况为我们上面所讲的多重继承,含虚函数覆盖的情况。此时大小计算为数据成员的大小+2个基类虚函数表指针大小
考虑字节对齐,结果为8+8+2*8=32
四.虚继承的情况
对虚继承层次的对象的内存布局,在不同编译器实现有所区别。
在这里,我们只说一下在gcc编译器下,虚继承大小的计算。
它在gcc下实现比较简单,不管是否虚继承,GCC都是将虚表指针在整个继承关系中共享的,不共享的是指向虚基类的指针。
class A { int a;
};class B:virtual public A{ virtual void myfunB(){}
};class C:virtual public A{ virtual void myfunC(){}
};class D:public B,public C{ virtual void myfunD(){}
};
以上代码中sizeof(A)=16,sizeof(B)=24,sizeof(C)=24,sizeof(D)=32.
解释:A的大小为int大小加上虚表指针大小。B,C中由于是虚继承因此大小为int大小加指向虚基类的指针的大小。B,C虽然加入了自己的虚函数,但是虚表指针是和基类共享的,因此不会有自己的虚表指针,他们两个共用虚基类A的虚表指针。D由于B,C都是虚继承,因此D只包含一个A的副本,于是D大小就等于int变量的大小+B中的指向虚基类的指针+C中的指向虚基类的指针+一个虚表指针的大小,由于字节对齐,结果为8+8+8+8=32。
类不能计算。
类的实质是一种数据类型,类似于int、char等基本类型,不同的是它是一种复杂的数据类型。因为它的本质是类型,而不是数据,所以不存在于内存中,不能被直接操作,只有被实例化为对象时,才会变得可操作。
类是对现实生活中一类具有共同特征的事物的抽象。如果一个程序里提供的类型与应用中的概念有直接的对应,这个程序就会更容易理解,也更容易修改。一组经过很好选择的用户定义的类会使程序更简洁。此外,它还能使各种形式的代码分析更容易进行。特别地,它还会使编译器有可能检查对象的非法使用。
类的内部封装了方法,用于操作自身的成员。类是对某种对象的定义,具有行为(be-havior),它描述一个对象能够做什么以及做的方法(method),它们是可以对这个对象进行操作的程序和过程。它包含有关对象动作方式的信息,包括它的名称、方法、属性和事件。
类的构成包括数据成员和成员函数。数据成员对应类的属性,类的数据成员也是一种数据类型,并不需要分配内存。成员函数则用于操作类的各项属性,是一个类具有的特有的操作,比如"学生"可以"上课",而"水果"则不能。类和外界发生交互的操作称为接口。
C++类的大小是怎样计算的
派生类的大小仍是基类和派生类的非静态数据成员的大小+一个vptr指针的大小 这样,我们就可以看到对于下面这样的程序, Base *b = new Derive(); b->f(); 由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。 (3)多继...
C++里是如何确定类的大小的?
所有数据成员的大小,加所有基类大小,加一个指向用于支持RTTI运行时类信息及虚函数的结构的指针.这是一般编译器的做法.C++允许编译器重排成员顺序,所以不会有像C中的为了对齐而加入的附加包.不过不建议猜测类大小,应用sizeof实测
如何查看java一个类的大小
基本java中一个类就是一个java文件.直接查看文件大小即可.在IDE Eclipse中选中目录中的一个类文件,直接按alt+enter就可以查看相关属性.另可以在操作系统的文件系统中查看相关类文件大小属性等 有些类可能包含内部类.这些另算.
怎样分辨狗狗体型大小?
1. 观察爪子大小:从小时候的爪子大小可以预测狗狗将来的体型。一般来说,小时候爪子较大的狗狗长大后的体型也会较大。2. 评估体型:狗狗在三个月大左右时,体型特征开始明显。小型犬此时身长一般在30至50厘米,而大型犬则在60至80厘米。3. 比较头部比例:大型犬幼犬的头部与身体比例通常比小型犬成犬...
主板大小是怎样的?
一般Micro-ATX主板大小是24.4x24.4cm,24.4x18.5cm,24.4x22.5cm。Micro-ATX实际上还包括:FlexATX(22.9 x 19.1cm)、uATX(24.5 x 18.5cm)等尺寸。扩展内容:电脑机箱主板,又叫主机板(mainboard)、系统板(systemboard)或母板(motherboard);它分为商用主板和工业主板两种。它安装在机箱内...
磁性氧化物都有那些,它们的磁性大小怎样
磁性材料按化学成份分,常见的有两大类:金属磁性材料和铁氧体.铁氧体是以氧化铁为主要成分的磁性氧化物.软磁性材料的剩磁弱,而且容易去磁.适用于需要反复磁化的场合.可以用来制造半导体收音机的天线磁棒、录音机的磁头、电子计算机中的记忆元件,以及变压器、交流发电机、电磁铁和各种高频元件的铁芯等...
...2和sin2°有什么区别?以及怎样比较这一类的大小? 这一章蛮抽象的...
但如果我们利用弧度的话,以上的式子将会变得更简单:(注意,弧度有正负之分) l=|α| r,即α的大小与半径之积。 同样,我们可以简化扇形面积公式 S=|α| r^2\/2(二分之一倍的α角的大小,与半径的平方之积,从中我们可以看出,当|α|=2π,即周角时,公式变成了S=πr^2,圆...
请问怎样才能从阀门的大小知道管道的大小?比如DN150的截止阀和DN80的...
其实你的问题的实质是公称直径与其他管径标注方法的对应关系。阀门一般都是用公称直径(DN***)标注的,比如DN150、DN100等。而管道有很多种标注方法,以外径、英制寸、公称直径等。只有把其他标注的尺寸转换成公称直径,那么阀门与管道就是一一对应了。常用的标注与公称直径的转换关系:1、塑料管类(U-...
怎样分罩杯大小
问题二:怎么看罩杯大小带图 您好!很高兴为你解答! 在没穿对文胸前,中国大部分是AB罩杯居多;在穿对文胸后,中国大部分是BC罩杯居多! 罩杯尺寸=上胸围-下胸围AA 约 7.5cmA 约 10cmB 约 12.5cmC 约 15cmD 约 17.5cmE 约 20cmF 约 22.5cmG 约 25cm H 约 27.5cm I 约30cm……(以此类推)注意测量...
sql中的文本类型日期如何比较大小
不知你比较,是想取出两者谁大谁小,还是取得两者之间的时间差 不管怎样,可以先把文本型转换成日期型:CONVERT(DATETIME,'200909'+'01',112) 你这个文本类型没有天,转换时会有问题,所以默认给加了个天,112是日期格式,有多种,想了解,具体可以查一下有哪些格式 CONVERT(DATETIME,'20080504',112...
程复康丽: 定义一个某类类型的变量a,然后sizeof(a)就是所占的内存,输出sizeof(a)就可以了.
潍坊市17155882080: C++里是如何确定类的大小的? - ?
程复康丽: 所有数据成员的大小,加所有基类大小,加一个指向用于支持RTTI运行时类信息及虚函数的结构的指针.这是一般编译器的做法.C++允许编译器重排成员顺序,所以不会有像C中的为了对齐而加入的附加包.不过不建议猜测类大小,应用sizeof实测
潍坊市17155882080: C++中类的大小是如何计算的? - ?
程复康丽: 其实就是一个数据对齐问题: 1,这个涉及到计算机底层.一般计算机组原理里面会介绍到.比如唐朔飞的那本就有涉及到. 2,在windows 核心编程 这本书中也提到了. 3,如果类是继承的,并且有虚函数,则类对象大小也不只是类中属性所...
潍坊市17155882080: 请问,c++中的类的大小如何确定??? - ?
程复康丽: shape类包含 int(4个字节) float(4个字节)char(1个字节)char类型指针(4个字节)所以shape的大小应该是4和1的最小公倍数 并且还要大于(4+4+4+1),所以应该是16.不论ca设置为多长的字符串,shape.ca存放的是字符串的首地址,只占四个字节.
潍坊市17155882080: C++,对象的大小 - ?
程复康丽: 因为只有数据成员有大小的概念,函数在那种不是数据的概念,叫做方法,不能用大小来衡量.那我类比一下吧,比如说一张白纸,你会说这张纸有多大,你不会说,哎,那个白色的长度是多少啊?对,颜色不能用长度来衡量.程序编译后当然是2进制数据,函数也是作为2进制数据存储的,但是c++语言语法不是针对2进制数据的,你要站在源代码的层次上来理解.
潍坊市17155882080: C++中,包含各种成员变量的类大小 - ?
程复康丽: 跟结构体是一样的,类可以看成一个特殊的结构的.为空时所占字节是1,其余要看类中变量的大小,如果你知道怎么算结构体的大小,类的也会算.
潍坊市17155882080: c++中类所占用的内存怎么算? - ?
程复康丽: 普通成员函数不占用类得内存.但是如果有虚函数的话,要有一个虚表指针的内存 类最小是占用一个字节.即一个没有任何成员变量和虚函数的类占用1字节
潍坊市17155882080: C++中数组大小怎么计算 - ?
程复康丽: 没有现成的函数, #include <stdio.h>int main() {int x[4][8];printf("%d,%d\n",sizeof x / sizeof x[0],sizeof x[0] / sizeof x[0][0]);return 0;} 我运行过了 ,应该这样
潍坊市17155882080: C++ 中 sizeof 如何计算 一个多重虚拟继承的类的大小? - ?
程复康丽: 简单,如果一个基类是虚基类,那么只有一个该类的实例,如果不是虚基类,那么每继承一次,就算一个实例.然后把所有实例的 size 加起来,就是最终派生类的 size 了.
潍坊市17155882080: C++ sizeof(类)的大小??
程复康丽: 类的大小主要和其成员变量以及虚拟函数指针有关,与普通成员函数无关 A没有任何成员变量和虚拟函数,相当于空类,内存中用1字节表示 B和C都虚拟继承A,因此都有一个虚拟继承表指针,因此大小为该指针大小4 D有两个虚拟继承表指针,因此大小为8