virtual inherit内存布局问题

本文起因是由于bird在gfriend里面法的帖子
c++问:一般normal derived class &  virtual derived class 的memory  storage有什么区别
作出解释

class  A
{
public:
    
int i;
}
;
class  B1: virtual   public  A
{

}
;

class  B2: virtual   public  A
{

}
;

class  C2: public  B1, public  B2
{

}
;


int  _tmain( int  argc, _TCHAR *  argv[])
{

    cout 
<< sizeof(A)<< endl;  // 4
    cout << sizeof(B1)<< endl;  // 8
    cout << sizeof(C2)<< endl;  // 12

    C2
* pc = new C2();
    A
* pa = static_cast<A*>(pc);
    B1
* pb1 = static_cast<B1*>(pc);
    B2
* pb2 = static_cast<B2*>(pc);

    cout 
<< pc << endl;  // forexample 002a9728
    cout << pa << endl;  // 002a9730
    cout << pb1 << endl;  // 002a9728
    cout << pb2 << endl;  // 002a972C
    int c;
    cin 
>> c;
    
return 0;
}

 从代码可以看出c的内存结构如下(12bit)
------
B1的内容
4bit 指针指向C
------
B2的内容
4bit 指针指向C
-------
C的内容
4bit 保存一个int
------
可以这样去理解
sizeof(A) is 4,这是i的空间,sizeof(b1) is 8,一个4bytes指向A的base指针+4byte的i。而SIZEOF(C)是12,两个指针和一个i。

有人说pc和pb1的指向同一内存地址?我们可以用下面的想法理解

pc和pb1指向统一address因为c中一定包含了b1,正好b1是c在最开始的地方.所以也是统一地址了,这个是继承的特性和virtual inherit无关
class A
{
 
}
class B:public A
{
}
 
B* pb = new B();
A* pa = static_cast<B*>(b)
 
pa and pb point to the same address too.
关于进一步的讨论,这两种继承的区别
再看下面的程序
class  A
{
public:
 
int i;
}
;
class  B1: virtual   public  A
{
public:
 
int i;
}
;

class  B2: virtual   public  A
{
public:
 
int i;
}
;

class  B3:  public  A
{
public:
 
int i;
}
;

class  B4:  public  A
{
public:
 
int i;
}
;

class  C1: public  B1, public  B2
{
public:
 
int i;
}
;

class  C2:  public  B3, public  B4
{
public:
 
int i;
}
;

 

int  _tmain( int  argc, _TCHAR *  argv[])
{
 cout 
<< sizeof(A) << endl; // 4
 cout << sizeof(B1) << endl; // 12
 cout << sizeof(B3) << endl; // 8
 cout << sizeof(C1) << endl; // 24 
 cout << sizeof(C2) << endl; // 20
}




其中A的内存结构
sizeof(A) == 4
----
1~4:A::i --- 4word
----
B1的内存结构(same as B2)
sizeof(B1) == 12 
-----
1~4: B1::A* 编译器产生的指向父类的指针因为虚继承指向父类的指针4word也就是指向9~12中的A
5~8: B1::i int 4 word
9~12:B1对象中的基类A的内容
----
C1的内存结构
sizeof(C1) == 24
----
(1~8是C1中的B1对象)
1~4: B1::A* 编译器产生的指向父类的指针因为虚继承指向父类的指针4word也就是指向20~24中的A
5~8: B1::i int 4 word
(9~16是C1中的B2对象)
9~12: B2::A* 编译器产生的指向父类的指针因为虚继承指向父类的指针4word也就是指向20~24中的A
12~16: B2::i int 4 word
(16~20是c1中的数据部分)
16~20 C1::i 4 word
(20~24是C1中的A)
20~24 A::i 4 word
----
我们看如果
B1* pb1 = new B1();
A* pa = static_cast<A*>(pb1);
会发生什么。
首先编译器会找到pb1的对象,把pb1对象的1~4字节指针的所指向的地址赋给b
如果
B1* pb1 = new C1();
A* pa = static_cast<A*>(pb1);
编译器也会进行相同的操作,并且指向正确位置(虽然偏移的地址量不同)

接下来我们可以看普通继承
B3的内存分布(B4类似)
sizeof(B3)8
----
(1~4为B3中的A)
1~4 A::i
(5~8)
5~8 B3::i
----
C2的内存分布
sizeof(C3)16
----
(1~4为B3中的A)
1~4 A::i
(5~8)
5~8 B3::i
(9~12为B4中的A)
9~12 A::i
12~16 B4::i
17~20 C2::i
----
大家可以看到如果不使用虚继承C2对象里面有2个A对象。所以以下代码不合法因为有2个A对象
 C2* c2 = new C2();
 A* pa = static_cast<A*>(c2);
但是我们可以看到下面代码合法
 C2* c2 = new C2();
 B3* pb1 = static_cast<B3*>(c2);
 B4* pb2 = static_cast<B4*>(c2);
 A* paOfB3 = static_cast<A*>(pb1);
 A* paOfB4 = static_cast<A*>(pb2);
注意paOfB3  != paOfB4.
按照C2内存分配的原则(paOfB4 - paOfB3) == 8
明白了。呵呵?

附小欣欣的解释还有我的跟贴
这是由于c++支持多重继承而引起的某些问题,例如

class A{
 public:
   void foo();
};

class B: public A{
};

class C: public A{
};

class D: public B, public C{
};

D *p = new D();

p->foo();

这是会触发编译错误,大体是因为有两个可供选择而不能作出决断的函数调用。


需要将普通集成改为虚拟继承。

小欣欣的意思是有了2义性。就是D中2个A对象的存在
如果修改如下就对了
 D *p = new D();
 p->C::foo(); // equal to p->C::A::foo();
p->B::foo();//equal to p->B::A::foo();

ps: 可能有些错误,但大体应该没错
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值