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

なぜこの違い?そして、なぜそれは2番目のケースの2倍なのですか?

4

4 に答える 4

2

これは実装に依存します。

ただし、ほとんどすべてのコンパイラは同じメカニズムを使用します。virtualキーワードがある場合は常に、コンパイラはとを介して追加の簿記を行う必要がvptrありvtablesます。この余分な簿記は、クラスのサイズを増やします。

sizeof厳密には、サイズを特定のものにする必要があります。これが、標準が推測ではなく実際のサイズを取得するために提供している理由です。

于 2012-12-08T10:59:07.527 に答える
2

非常に簡単に言えば、仮想継承には余分なオーバーヘッドが伴います。通常の実装では、少なくとも追加のポインターが必要になります。

複数の仮想継承と型キャストおよびその回答については、仮想テーブルと仮想ポインターの質問4を参照してください。

于 2012-12-08T10:59:17.223 に答える
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;
};

タイプのオブジェクトには、の定義からとの定義からのC2つのintメンバーがあります。同様に、タイプのオブジェクトには2つのメンバーとがあります。ここまでは順調ですね。トリッキーな部分にはクラスがあります。の両方のインスタンスが仮想ベースであるため、クラスにも1つのメンバーがあります。したがって、上記のように書くこともできません。これは、のコピーが2つになるためです。kCiADintliE intiACDB0Ei

解決策は、間接参照のレイヤーを追加することです。Cタイプ、、のオブジェクトは次のようDになりEます(擬似コード、コンパイルしないでください)。

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の単一のコピーを持つことができます。(実際には、これらの各ポインターは、へのポインターになります。これは、確かに複数のデータメンバーを持つことができるためですが、この単純な擬似コードで表すのは難しすぎます)。iCDAA

于 2012-12-08T13:33:51.287 に答える
0

コンパイラの実装によって異なります。コンパイラが異なれば、結果も異なります。しかし、1つは確かであり、結果は3つ以上でなければなりません。

于 2012-12-08T12:17:25.103 に答える