6
#include<iostream>

using namespace std;

class abc
{
    int a;
};
class xyz : public virtual abc
{
    int b;
};

int main()
{
    abc obj;
    xyz obj1;
    cout<<endl<<sizeof(obj);
    cout<<endl<<sizeof(obj1);
    return 0;
}

答えはコンパイラに依存しますが、これを結果として見たときは驚きました

~/Documents/workspace/tmp ‹.rvm-›  $ ./class_sizes   

4
16

virtual キーワードを削除すると、割り当てられるサイズはそれぞれ 4 と 8 になり、これは予想どおりです。

余分なスペースが正確に占有されているのはなぜですか? vptr テーブルまたはそのようなもののためだと思われますが、確かなことはわかりません。

4

5 に答える 5

7

GCC での仮想継承と多重継承に関する優れた記事は次のとおりです(インターネット アーカイブのパーマリンク) 。

http://phpcompiler.org/articles/virtualinheritance.html

それでも、使用している (未指定の) コンパイラとビルド設定から 20 バイトの出力を取得しているため、質問には完全には答えません。

GCC を使用していた場合(少なくとも IDEone が使用するデフォルト設定では)、12 バイトを取得します。これは、あなたが書いた場合に得られるものと同じです:

class abc
{
    int a;
    virtual void foo() {}
};
class xyz : public abc
{
    int b;
};

abc に仮想メソッドが含まれている場合、abc から仮想的に継承しますか?

class abc
{
    int a;
    virtual void foo() {}
};
class xyz : virtual public abc
{
    int b;
};

...次に、GCC から 16 バイトを取得します。

余分なスペースが正確に占有されているのはなぜですか? vptr テーブルまたはそのようなもののためだと思われますが、確かなことはわかりません。

あなたの 16 バイトの差異について大雑把な推測をしなければならなかった場合:コンパイラの仮想継承の実装が、仮想メソッドを持っていない場合でも、すべての仮想基本クラスを仮想メソッドを持っているかのように扱っているかどうかを調べることができますか?

しかし、私はそれをかなり作りました。理論をテストしたい場合は、ボンネットの下をさらに調べる必要があります。それは実装に依存します。

于 2011-11-12T16:47:48.263 に答える
3

仮想基本クラスは (動的、実行時) 変換要件を増やします。サイズの増加は、そのような変換を行うときに、(基本) クラス階層をあいまいでない方法で移動するための一種の「ピボットグラウンド」のためのものだと思います。

少し大げさではありませんが、何が起こっているのかを示すことができる反例を次に示します。

 

#include<iostream>

class abc
{
    int x;
    virtual void t();
};

template <int unique> struct interm : virtual abc 
{
    virtual void t();
    virtual void s();
};

struct xyz : 
    /*virtual*/ interm<1>, 
    /*virtual*/ interm<2>, 
    /*virtual*/ interm<3>, 
    /*virtual*/ interm<4>,
    /*virtual*/ interm<5>, 
    /*virtual*/ interm<6>, 
    /*virtual*/ interm<7>, 
    /*virtual*/ interm<8>
{
    int b;
    virtual void t();
    virtual void s();
};


int main()
{
    std::cout << sizeof(abc)       << std::endl;
    std::cout << sizeof(interm<1>) << std::endl;
    std::cout << sizeof(xyz)       << std::endl;
    return 0;
}

ベースを仮想としてマークすると、サイズが大幅に縮小されることに気付くでしょうabc(少なくとも gcc では)。また、intermediate 基本クラスの (いずれかの) を (非) 仮想としてマークしても効果がないことに注意してください。

于 2011-11-12T16:45:42.200 に答える
2

仮想基本クラスでは、派生オブジェクトのインスタンスに対する基本オブジェクトの位置が常に同じであるとは限らないため、それを追跡するポインターがあります。

于 2011-11-12T16:50:32.147 に答える
2

非仮想継承は、次の場合、オブジェクトの包含と同じです。

struct Derived : Base

次の方法で C++ にコンパイルできます。

struct Derived {
    Base __base;
    // other members

    // implementation of Derived-to-Base pointer conversion
    operator Base& () { return __base; }
};

仮想継承は、間接的なレベルを追加するようなものです。

struct Base
struct L : virtual Base
struct R : virtual Base
struct Derived : L, R

これは、次のように C++ にコンパイルできます。

// the type L& is translated to __L_subobject&
// the type L* is translated to __L_subobject*
// lvalue of L is translated to lvalue of __L_subobject
struct __L_subobject {
    Base &__base_ref;
    __L_subobject (Base &__base_ref) 
        : __base_ref(__base_ref) {
    }
    // other members

    // pointer conversions:
    operator Base& () { return __base_ref; }
};

// a definition of variable x of type L is translated to one with type __L_complete
// but any lvalue x is translated to x.__most_derived
// (it is assumed that rvalues have been already been translated to lvalues)
struct __L_complete {
    // all virtual bases:
    Base __base;

    // derived partial subobjects:
    __L_subobject __most_derived;

    __L_complete () : __most_derived(__base) {}
};

// ... same for R ...

struct __Derived_subobject {
    __L_subobject __L;
    __R_subobject __R;
    // other members ...

    __Derived_subobject (Base &__base_ref) 
        : __L(__base_ref),
          __R(__base_ref) {
    }

    // pointer conversions:
    operator Base& () { return __L.operator Base& (); }
    operator __L_subobject& () { return __L; }
    operator __R_subobject& () { return __R; }
};

struct __Derived_complete {
    // all virtual bases:
    Base __base;

    // derived partial subobjects:
    __Derived_subobject __most_derived;

    __Derived_complete () :__most_derived(__base) {
    }
};

あなたはアイデアを得る...

注: vtable ポインター メンバーについては説明していません。Base&(より小さなクラスを持つために、の代わりに使用できます。)

于 2011-12-26T05:46:01.610 に答える