仮想機能を追加して追加がD
必要
になるのはなぜvptr
ですか? クラス イメージは または のいずれかで始まり、その関数をいずれB
かC
に
追加することができます。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 でコンパイルされています。結果は簡単に異なる可能性があります。)