1

虽然与继承玩耍,正好试试这个:在多级继承中派生的虚拟基类会发生什么?

class A 
{ int i; }; 

class B : virtual public A 
{ int j; }; 

class C : public B 
{ int k; }; 

int main() 
{ 
    std::cout<<sizeof(C)/sizeof(int); 
    return 0; 
} 

这给了我输出6

虽然下面的预期一样给输出3

class A 
{ int i; }; 

class B : public A // No virtual here 
{ int j; }; 

class C : public B 
{ int k; }; 

int main() 
{ 
    std::cout<<sizeof(C)/sizeof(int); 
    return 0; 
} 

为什么这个区别 ?而且,为什么它是第二种情况的两倍?

回答

1
class A { 
    int i; 
}; 

class B : public A { 
    int j; 
}; 

在这个例子中,它不使用虚拟继承,B类型的对象可以被布置为如果B已定义是这样的:

class B0 { 
    int i; 
    int j; 
}; 

一旦你介绍虚拟继承,这是行不通的:

class C : public virtual A { 
    int k; 
}; 

class D : public virtual A { 
    int l; 
}; 

class E : public C, public D { 
    int m; 
}; 

C类型的对象有两个int成员:k来自Ci的定义A。同样,D类型的对象有两个int成员,li。到现在为止还挺好。棘手的部分来自类E:它也有一个int成员i,因为A的两个实例都是虚拟基地。所以既不是C也不是D不能像上面的B0一样写,因为那么E最终会以两个副本i

解决方法是添加一个间接层。C类型,DE是这个样子的对象(伪代码,不要试图将其编译):

class C0 { 
    int *cip = &i; 
    int k; 
    int i; 
}; 

class D0 { 
    int *dip = &i; 
    int l; 
    int i; 
}; 

class E0 { 
// C0 subobect: 
    int *cip = &i; 
    int k; 
// D0 subobject: 
    int *dip = &i; 
    int l; 
// E data: 
    int *eip = &i; 
    int m; 
    int i; 
}; 

你在E尺寸看到的是那些额外的指针,这无论如何将CD合并到派生类中,都可以拥有i的单个副本。 (实际上,这些指针中的每一个都是指向A的指针,因为A当然可以有多个数据成员,但在这个简单的伪代码中很难表示)。

+0

'int * cip =&i;'通过这个你的意思是指向C中的继承成员我的指针?如果是这样,那么在虚拟继承中,实际上只有指针被继承,而不是具有相同名称的成员? – asheeshr

+1

@AshRj - 这个答案是关于实现的细节。不管如何实现虚拟继承,虚拟基础的成员都是继承的;您可以直接通过派生类型的对象来引用它们,而不必知道它们来自基类。 –

2

这是执行依赖

然而,几乎所有的编译器将使用相同的机制,只要你有一个virtual关键字编译器需要通过vptrvtables做一些额外的簿记。这额外的簿记增加了班级人数。

严格来说,您应该依赖尺寸来确定任何特定尺寸,这就是为什么标准提供sizeof以获得实际尺寸而不是猜测它的原因。

0

这取决于你的编译器的实现。不同的编译器有不同的结果但可以肯定的是,结果必须超过三个。

相关问题