1
#include "stdafx.h"
#include <iostream>
using namespace std;

class ClassA
{
    protected:
       int width, height;
    public:
       void set_values(int x, int y)
       {
         width = x;
         height = y;
       }
};

class ClassB : virtual public ClassA
{
   //12(int + int + vptr)
};

class ClassC : virtual public ClassA
{
  //12(int + int + vptr)
};

class ClassD : public ClassB, public ClassC
{
};

int main()
{
  ClassA A;
  ClassB B;
  ClassC C;
  ClassD D;
  cout << "size = " << sizeof(A) << endl;
  cout << "size = " << sizeof(B) << endl;
  cout << "size = " << sizeof(C) << endl;
  cout << "size = " << sizeof(D) << endl;
  return 0;
}

私が得た出力は次のとおりです。

size of ClassA = 8
size of ClassB = 12
size of ClassC = 12
size of ClassD = 16

上記のコードでは、ClassD の出力が 16 である理由です。この仮想継承がどのように機能するかを明確に説明してください。

4

3 に答える 3

3

仮想継承とは、仮想基本クラスが複数回ではなく一度だけ存在することを意味します。そのため、からの 8 バイトClassAClassD1 回だけです。仮想継承自体には一定のオーバーヘッドが必要なため、追加のポインターを取得します。正確な実装、つまり正確なオーバーヘッドは C++ 標準では指定されておらず、作成する階層によって異なる場合があります。

于 2013-03-11T09:45:20.227 に答える
2

ClassD が ClassB と ClassC を継承する場合、2 つの vptr (B から 1 つと C から 1 つ) が存在します。この正確なケースは、Scott Meyers の「より効果的な C++」の項目 24 (さまざまな言語機能のコスト) で説明されています。

于 2013-03-11T09:44:05.390 に答える
0

Virtual base classes implementations

Virtual base classes are exactly like virtual function: their address (or relative address aka offset) is not known at compile time:

void f(ClassB *pb) {
    ClassA *pa = pb;
}

Here the compiler must compute the offset of the ClassA base subobject from the ClassB subobject (or mostly derived object). Some compiler simply have a pointer to it inside ClassB; others use the vtable, just like for virtual functions.

In both case the overhead in ClassB is one pointer.

The ClassC is similar, but the vptr will be point to a ClassC vtable, not a ClassB vtable.

Thus a ClassD object will contain (this is not an ordered list):

  • a single ClassA subobject
  • a ClassB subject
  • a ClassC subject

So ClassD has two inherited vptr: from ClassB and ClassC. In a ClassD object, both vptr will point to some ClassD vtable, but the same ClassD vtable:

  • a ClassB subject points to the ClassB-in-ClassD vtable, which indicates the relative position of ClassA base from ClassB base
  • a ClassC subject points to the ClassC-in-ClassD vtable, which indicates the relative position of ClassA base from ClassC base

Possible optimization

I guess your question is: do we need two distinct vptr?

Technically, it is sometimes possible to optimize the size of classes by overlaying base class subobjects. This is one of those cases where it is technically possible:

Overlaying (or unification) means that both ClassB and ClassC will share the same vptr: given d an instance of ClassD: &d.ClassB::vptr == &d.ClassC::vptr so d.ClassB::vptr == d.ClassC::vptr but d.ClassB::vptr == &ClassC_in_ClassD_vtable and d.ClassC::vptr == &ClassC_in_ClassD_vtable, so ClassB_in_ClassD_vtable must be unified with ClassC_in_ClassD_vtable. In this particular case, both ClassB_in_ClassD_vtable and ClassC_in_ClassD_vtable are only used to describe the offset of ClassA subobject; if ClassB and ClassC subobjects are unified in ClassD, then these offsets are unified too, hence the unification of the vtables are possible.

Note that this is only possible here as there is perfect similarity. If ClassB and ClassC were modified to add even one virtual function in each, such as these virtual functions are not equivalent (hence not unifiable), the vtable unification would not be possible.

Conclusion

This optimization is only possible in very simple cases like this one. These cases are not typical of C++ programming: people normally use virtual base classes in combination with virtual functions. Empty base class optimization is useful because many C++ idioms use base classes with no data members or virtual functions. OTOH, a tiny (one vptr) space optimization for a special use of virtual base classes does not seem useful for real world programs.

于 2013-06-16T01:54:08.737 に答える