场景 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;
}