C++:C++ 空类的大小及相关继承类的大小
2015-02-05 16:03
176 查看
一个C++空类实例化大小事实上并不空,它有一个隐晦的1个byte.
首先:何为类的实例化?
所谓类的实例化,就是在内存中分配一块地址。
例一:
为什么会出现这种结果呢?
类A,B明明是空类,它的大小应该为为 0,为什么 编译器输出的结果为1呢?这就是我们刚才所说的实例化的原因(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址.所以A,B的大小为1.
类C是由类A派生而来,它里面有一个纯虚函数,由于有虚函数的原因,有一个指向虚函数的指针(vptr),在32位的系统分配给指针的大小为4个字节,所以最后得到C类的大小为4.
类D是由类B,C派生迩来的,它的大小应该为二者之和5,为什么却是8呢?这是因为为了提高实例在内存中的存取效率.类的大小往往被调整到系统的整数倍.采取对长补齐,采用就近最近的整数倍数,就是该类的大小,所以类D的大小为8个字节.
当然在不同的编译器上上述得到的结果可能会不同。
例二:
因为:
类B的静态数据成员被编译器放在程序的一个global data segment中,它是类的一个数据成员.但是它不影响类的大小,不管这个类实际产生了多少实例,还是派生了多少新的类,静态成员数据在类中永远只有一个实体存在,而类的非静态数据成员只有被实例化的时候,他们才存在.但是类的静态数据成员一旦被声明,无论类是否被实例化,它都已存在.可以这么说,类的静态数据成员是一种特殊的全局变量.
所以A,B的大小相同.
例三:
现在来看一个有构造函数,和析构函数的类的大小!
例四:
如:
类Y,类Z的大小受三个因素的影响:
1)语言本身所造成的额外负担:因为类Y与类Z都是公有虚继承于类X,这里将有vptr,在32位上为4个字节。
2)编译器对特殊情况所做的优化处理:这里做了优化,一个空的虚基类被放在了派生类的最开头的部分,也就是说它未花费任何额外的空间,这就节省了类X的1字节,
如未做优化:一个空的虚基类被放在了派生类的固定不变动的尾端。
3)Alignment 对长补齐的限制:对长补齐,上述未做优化,则虚基类X的1个字节与类Y的4个字节(共5个字节),补齐3个字节,最终为8个字节。
做优化,则虚基类X的1个字节将被省略,只有类Y的4个字节(共4个字节),不需要补齐,最终为4个字节。
注:补齐就是将数值调整为某个整数的整数倍,在32位机器上,一般Alignment 为 4 个字节。
记住:一个虚基类对象只会在派生类中存在一份实体,不管它在继承体系中出现了多少次!
类A的大小:
1)类X,所有继承类唯一共享的实体,大小为1 type.
2)类Y:做了优化,只有4 type
3)类Z:同上,只有4 type
4)类A:1 + 4 + 4 = 9 type,补齐为12 type
从以上的几个例子总结类的大小:
1).为类的非静态成员数据的类型大小之和.
2).有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针).
3).为了优化存取效率,进行的边缘调整.
4).与类中的构造函数,析构函数以及其他的成员函数无关.
首先:何为类的实例化?
所谓类的实例化,就是在内存中分配一块地址。
例一:
#include<iostream> using namespace std; class A {}; class B {}; class C : public A { virtual void c_fun()=0; }; class D : public B, public C {}; int main() { cout<<"sizeof(A):"<<sizeof(A)<<endl; cout<<"sizeof(B):"<<sizeof(B)<<endl; cout<<"sizeof(C):"<<sizeof(C)<<endl; cout<<"sizeof(D):"<<sizeof(D)<<endl; return 0; }输出:
sizeof(A):1 sizeof(B):1 sizeof(C):4 sizeof(D):8 请按任意键继续. . .
为什么会出现这种结果呢?
类A,B明明是空类,它的大小应该为为 0,为什么 编译器输出的结果为1呢?这就是我们刚才所说的实例化的原因(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址.所以A,B的大小为1.
类C是由类A派生而来,它里面有一个纯虚函数,由于有虚函数的原因,有一个指向虚函数的指针(vptr),在32位的系统分配给指针的大小为4个字节,所以最后得到C类的大小为4.
类D是由类B,C派生迩来的,它的大小应该为二者之和5,为什么却是8呢?这是因为为了提高实例在内存中的存取效率.类的大小往往被调整到系统的整数倍.采取对长补齐,采用就近最近的整数倍数,就是该类的大小,所以类D的大小为8个字节.
当然在不同的编译器上上述得到的结果可能会不同。
例二:
#include<iostream> using namespace std; class A{ private: int data; }; class B{ private: int data; static int data1; }; int B::data1=0; void main() { cout<<"sizeof(A) = "<< sizeof(A) <<endl; cout<<"sizeof(B) = "<< sizeof(B) <<endl; }输出结果:
sizeof(A) = 4 sizeof(B) = 4 请按任意键继续. . .为什么类B多了一个数据成员,却大小和类A的大小相同呢?
因为:
类B的静态数据成员被编译器放在程序的一个global data segment中,它是类的一个数据成员.但是它不影响类的大小,不管这个类实际产生了多少实例,还是派生了多少新的类,静态成员数据在类中永远只有一个实体存在,而类的非静态数据成员只有被实例化的时候,他们才存在.但是类的静态数据成员一旦被声明,无论类是否被实例化,它都已存在.可以这么说,类的静态数据成员是一种特殊的全局变量.
所以A,B的大小相同.
例三:
现在来看一个有构造函数,和析构函数的类的大小!
#include<iostream> using namespace std; class A{ public : A(int a) { a=x; } void f(int x) { cout<<x<<endl; } ~A(){} private: int x; int g; }; class B{ public: private: int data; int data2; static int xs; }; int B::xs=0; void main() { A s(10); s.f(10); cout << "sizeof(a): "<< sizeof(A) << endl; cout << "sizeof(b): "<< sizeof(B) << endl; }输出结果:
10 sizeof(a): 8 sizeof(b): 8 请按任意键继续. . .它们的结果均相同,可以看出类的大小与它当中的构造函数,析构函数,以及其他的成员函数无关,只与它当中的成员数据有关.
例四:
#include<iostream> using namespace std; class X {}; class Y : public virtual X {}; class Z : public virtual X {}; class A : public Y, public Z {}; // 上述为经典的钻石继承 int main() { cout << "sizeof(X): " << sizeof(X) << endl; cout << "sizeof(Y): " << sizeof(Y) << endl; cout << "sizeof(Z): " << sizeof(Z) << endl; cout << "sizeof(A): " << sizeof(A) << endl; return 0; }输出结果:
sizeof(X): 1 sizeof(Y): 4 sizeof(Z): 4 sizeof(A): 8 请按任意键继续. . .如例一一样,类X是一个空的类,但它事实上并不是空的,它有一个隐晦的1 type,那是被编译器安插进去的一个char,它主要使这个类的对象可以在内存中配置一个独一无二的地址。
如:
X a, b; if (&a == &b) { cerr << "oh, my god!" << endl; }
类Y,类Z的大小受三个因素的影响:
1)语言本身所造成的额外负担:因为类Y与类Z都是公有虚继承于类X,这里将有vptr,在32位上为4个字节。
2)编译器对特殊情况所做的优化处理:这里做了优化,一个空的虚基类被放在了派生类的最开头的部分,也就是说它未花费任何额外的空间,这就节省了类X的1字节,
如未做优化:一个空的虚基类被放在了派生类的固定不变动的尾端。
3)Alignment 对长补齐的限制:对长补齐,上述未做优化,则虚基类X的1个字节与类Y的4个字节(共5个字节),补齐3个字节,最终为8个字节。
做优化,则虚基类X的1个字节将被省略,只有类Y的4个字节(共4个字节),不需要补齐,最终为4个字节。
注:补齐就是将数值调整为某个整数的整数倍,在32位机器上,一般Alignment 为 4 个字节。
记住:一个虚基类对象只会在派生类中存在一份实体,不管它在继承体系中出现了多少次!
类A的大小:
1)类X,所有继承类唯一共享的实体,大小为1 type.
2)类Y:做了优化,只有4 type
3)类Z:同上,只有4 type
4)类A:1 + 4 + 4 = 9 type,补齐为12 type
从以上的几个例子总结类的大小:
1).为类的非静态成员数据的类型大小之和.
2).有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针).
3).为了优化存取效率,进行的边缘调整.
4).与类中的构造函数,析构函数以及其他的成员函数无关.
相关文章推荐
- C++中各种空类以及继承空类后的sizeof大小
- 【C++面向对象】类的大小以及虚继承
- c++空类实例大小不是0原因(zz)
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- c++空类实例大小不是0原因
- 空类,虚函数类,虚继承类的空间大小
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++空类的大小
- c++空类的大小
- c++空类实例大小不是0原因
- 大小测试(空类、带含静态和非含、虚和非虚的单继承和多继承等)
- C++继承、虚继承、虚函数类的大小问题
- c++空类实例大小不是0原因)
- c++ - 为什么空类实例大小不是0
- C++多重继承相关问题
- 类大小测试(空类、带含静态和非含、虚和非虚的单继承和多继承等)
- c++空类实例大小不是0原因