C++ Data语义学
场景 1 虚拟继承
#include <iostream>
class A {static int s;};
class B: virtual public A {};
class C: virtual public A {};
class D: public B, public C{char a;};
int main(int argc , char* argv[])
{
std::cout << "A: " << sizeof(A) << std::endl;
std::cout << "B: " << sizeof(B) << std::endl;
std::cout << "C: " << sizeof(C) << std::endl;
std::cout << "D: " << sizeof(D) << std::endl;
return 0;
}
ubuntu 64bits g++ 5.4.0 结果:
A: 1 // static变量不占object的内存!
B: 8
C: 8
D: 24 //8 + 8 + 1 + 7
A: 编译器插入 1 字节,表明 object 的唯一地址用的;
B:C 加上 vptr,但是又不需要那 1 字节了?
D: 继承 ,两个 vptr !(virtual public 不起作用?)
结论: 编译器行为…很不好说.可能自己插入一些字节, 另外还有可能有需要补齐也占用 object 的字节.
data member 的布局
class E {
public:
virtual ~E(){}
int a;
char b;
void * c;
virtual void foo() {}
};
E e;
std::cout << "E: " << sizeof(E) << std::endl;
std::cout << "e.addr " << &e << std::endl;
std::cout << "e.a " << &(e.a) << std::endl;
std::cout << "e.b " << &(e.b) << std::endl;
std::cout << "e.c " << &(e.c) << std::endl;
结果是:
E: 24 = 8 +4 + 1 + 3 + 8
e.addr 0x7ffd47bb2c50
e.a 0x7ffd47bb2c58
e.b
e.c 0x7ffd47bb2c60
E 的大小为 size(vptr) + size(int) + size(char) + size(void*)=24.
但有的编译器, 不一定把 vptr 放在开头的位置.也可能放在结尾, a,b,c 这些 data member 的顺序也可能在任意位置.
继承类的 data member 的布局
-
一般来说就是 base class data member + derived class new data member.但是 data member 的布局也不是确定的,仍然取决于编译器.
-
多重继承,可能带来多重 padding,(如派生每次都多出一个 char 变量,需要补齐为 4),造成空间的浪费.
-
如果 base class 没有 vitural function ,而 drived class 有:
Base b;
Derived d & = b;
编译器得在编译器介入,为 d 插入 vptr.
- 多重继承
#include <iostream>
class A {int m_a; char m_ca;};
class B {int m_b;};
class C :public A, public B { int m_c;};
int main(int argc , char* argv[])
{
A a;
B b;
C c;
A* pa = &c;
B* pb = &c;
std::cout << "c addr: " << &c << std::endl;
std::cout << "pa addr: " << pa << std::endl;
// pb = (B*)(&c + sizeof(A))
std::cout << "pb addr: " << pb << std::endl;
return 0;
}