0
#include<iostream>    
using namespace std;

class A
{
    public:
        int i;
};

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

class C: virtual public A
{
    public:
        int k;
};
class D: virtual public B, virtual public C
{
    public:
        int l;
};

int main()
{

    cout<<" A : "<<sizeof(A)<<endl;
    cout<<" B : "<<sizeof(B)<<endl;
    cout<<" C : "<<sizeof(C)<<endl;
    cout<<" D : "<<sizeof(D)<<endl;
}

出力:

1
8
8
16

私の理解によると、仮想継承中、すべての基本クラスには 1 つの vptr があるため、クラス D には 2 つの vptr があり、サイズは 16 です。

クラス D にも仮想関数がある場合、virtual ~D() と仮定します。ここで、サイズを 24 に増やす必要があります (D 用に vptr が 1 つ)。

しかし、それは起こっていません。

仮想ptrの概念をどのように誤解していますか。

わかりやすく説明できる人いますか??

4

2 に答える 2

1

DB仮想関数があるかどうかに関係なく、vptr が必要Cです。

その理由は、コンパイラが の任意のインスタンスDからそのABまたはC、基本クラスのサブオブジェクトを取得する方法を必要とするためです。その継承は仮想であるため、同じ仮想ベースを持つ他のものから派生するだけでなく、他のものからも派生するクラスEが発生する可能性があります。のレイアウトは、その存在の可能性を考慮しなければなりません。DDE

Dしたがって、この完全なオブジェクトが as-まだ未定義の のインスタンスである可能性があるため、 のインスタンスである完全なオブジェクトからその基本クラスのサブオブジェクトに取得するための固定オフセットはありませんE

それがあなたが見ている行動を完全に説明しているかどうかはわかりません。私は which の実装を使用したことがsizeof(int) == 1なく、その理由を説明する根拠が実際にはなく、B8Dと 16 です。

于 2013-02-07T17:56:49.827 に答える
0

仮想機能を追加して追加がD必要 になるのはなぜvptrですか? クラス イメージは または のいずれかで始まり、その関数をいずれBCに 追加することができます。D(そして、仮想継承があるとすぐに、仮想関数がなくても、ある種のポインターが必要になります。)

に仮想関数を追加するA 違いが生じます (もちろん、ここにあるものはすべて実装に依存しますが)。価値があるので、次のようなことを試してみてください。

#include <iostream>
#include <iomanip>
#include <cstdint>

typedef std::uintptr_t Word;

class SaveIOFormat
{
    std::basic_ios<char>* myStream;
    char myFill;
    std::basic_ios<char>::fmtflags myFlags;
    int myPrecision;
public:
    SaveIOFormat( std::basic_ios<char>& stream )
        : myStream( &stream )
        , myFill( stream.fill() )
        , myFlags( stream.flags() )
        , myPrecision( stream.precision() )
    {
    }
    ~SaveIOFormat()
    {
        myStream->fill( myFill );
        myStream->flags( myFlags );
        myStream->precision( myPrecision );
    }
};

template <typename T>
class DumpAsWords
{
    Word const* myObj;
    typedef Word const* Iterator;
    typedef char sizeMustBeMultipleOfSizeofWord
            [ sizeof(T) % sizeof(uintptr_t) == 0 ? 1 : -1 ];
    static int const ourLength = sizeof(T) / sizeof(Word);
public:
    DumpAsWords( T const& obj )
        : myObj( reinterpret_cast<Word const*>( &obj ) )
    {
    }
    friend std::ostream& operator<<( std::ostream& dest,
                                     DumpAsWords const& obj )
    {
        SaveIOFormat saveExcursion( dest );
        dest.fill( '0' );
        dest.setf( std::ios::hex, std::ios::basefield );
        for ( Iterator current = obj.myObj, end = obj.myObj + ourLength;
                current != end;
                ++ current ) {
            if ( current != obj.myObj ) {
                dest << ' ';
            }
            dest << std::setw( sizeof(Word) * 2 ) << *current;
        }
        return dest;
    }
};

template <typename T>
DumpAsWords<T>
dump( T const& obj )
{
    return DumpAsWords<T>( obj );
}

class B
{
    Word i;
public:
    B() : i( 1 ) {}
    virtual ~B() {}
};

class L : virtual public B
{
    Word i;
public:
    L() : i( 2 ) {}
};

class R : virtual public B
{
    Word i;
public:
    R() : i( 3 ) {}
};

class D : public L, public R
{
    Word i;
public:
    D() : i( 4 ) {}
};

int
main()
{
    D aD;

    std::cout << "sizeof B: " << sizeof(B) << std::endl;
    std::cout << "sizeof L: " << sizeof(L) << std::endl;
    std::cout << "sizeof R: " << sizeof(R) << std::endl;
    std::cout << "sizeof D: " << sizeof(D) << std::endl;
    std::cout << std::endl;

    std::cout << "addrof B: " << static_cast<B*>( &aD ) << std::endl;
    std::cout << "addrof L: " << static_cast<L*>( &aD ) << std::endl;
    std::cout << "addrof R: " << static_cast<R*>( &aD ) << std::endl;
    std::cout << "addrof D: " << static_cast<D*>( &aD ) << std::endl;
    std::cout << std::endl;

    std::cout << "dump: " << dump( aD ) << std::endl;
    return 0;
}

(少し長い場合は、ライブラリからいくつかのコードをコピーして貼り付けて、よりシンプルでクリーンにしたためです。)

私のマシンでは、これにより次のようになります。

sizeof B: 16
sizeof L: 32
sizeof R: 32
sizeof D: 56

addrof B: 00000000001AFEB8
addrof L: 00000000001AFE90
addrof R: 00000000001AFEA0
addrof D: 00000000001AFE90

dump: 000000013fb90bb0 0000000000000002 000000013fb90bb8 0000000000000003 0000000000000004 000000013fb90ba8 0000000000000001

ご覧のとおり、順序は L in D、R in D、D、B (L、R、D) です。D は vptr を L と共有します。

(これは 64 ビット Windows マシンで VC++ 11 でコンパイルされています。結果は簡単に異なる可能性があります。)

于 2013-02-07T18:41:34.290 に答える