虚拟继承C ++中的sizeof

时间:2013-02-07 17:46:42

标签: c++

#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

根据我的理解,在虚拟继承期间,每个基类都有一个vptr,因此类D有两个vptr,大小为16。

假设D类也有虚拟函数假设虚拟~D()。现在大小应该增加到24(D的一个vptr)。

但事实并非如此。

我有些误解虚拟ptr概念。

任何人都可以解释清楚吗?

2 个答案:

答案 0 :(得分:1)

D需要一个vptr,无论它是否具有任何虚函数,都与BC一样多。

原因是编译器需要一种方法从D的任何实例到其ABC基类子对象。由于该继承是虚拟的,因此某些类E可能来自D,但来自具有相同虚拟基础的其他事物。 D的布局必须考虑到E存在的可能性。

因此,从作为D实例的完整对象到其基类子对象,没有固定的偏移量,因为这个完整对象可能是as-yet-的一个实例。未定义E

我不确定这是否完全说明了你看到的行为。我从未使用sizeof(int) == 1的实现,而且我没有任何理由来解释为什么BD为8和16。

答案 1 :(得分: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

正如你所看到的,顺序是D中的L,D中的R,D,B(L,R和 d)。 D与L共享vptr。

(这是在64位Windows机器上用VC ++ 11编译的。你的结果很容易就会有所不同。)