virtual在C++中最大的功能就是声明虚函数和虚基类。虚拟继承是为了解决多重继承下公共基类的多份拷贝问题。
虚继承
虚继承解决了菱形继承中对派生类拥有多个间接父类实例的情况。虚继承的派生类的内存布局与普通继承很多不同,主要体现在:
虚继承的子类,如果本身定义了新的虚函数,则编译器为其生成一个虚函数指针(vptr)以及一张虚函数表。该vptr位于对象内存最前面。vs非虚继承:直接扩展父类虚函数表。
虚继承的子类也单独保留了父类的vprt与虚函数表。这部分内容接与子类内容以一个四字节的0来分界。
虚继承的子类对象中,含有四字节的虚表指针偏移值。
虚基类表解析
在C++对象模型中,虚继承而来的子类会生成一个隐藏的虚基类指针(vbptr),在MicrosoftVisualC++中,虚基类表指针总是在虚函数表指针之后,因而,对某个类实例来说,如果它有虚基类指针,那么虚基类指针可能在实例的0字节偏移处(该类没有vptr时,vbptr就处于类实例内存布局的最前面,否则vptr处于类实例内存布局的最前面),也可能在类实例的4字节偏移处。
一个类的虚基类指针指向的虚基类表,与虚函数表一样,虚基类表也由多个条目组成,条目中存放的是偏移值。第一个条目存放虚基类表指针(vbptr)所在地址到该类内存首地址的偏移值,由第一段的分析我们知道,这个偏移值为0(类没有vptr)或者-4(类有虚函数,此时有vptr)。我们通过一张图来更好地理解。
虚基类表的第二、第三...个条目依次为该类的最左虚继承父类、次左虚继承父类...的内存地址相对于虚基类表指针的偏移值,这点我们在下面会验证。
单虚拟继承
class Base
{
public:
virtual void fun1()
{
cout << "Base::func1()" << endl;
}
virtual void fun2()
{
cout << "Base::func2()" << endl;
}
private:
int b;
};
class Derive : virtual public Base
{
public:
virtual void fun1()
{
cout << "Derive::func1()" << endl;
}
virtual void fun3()
{
cout << "Derive::func3()" << endl;
}
void fun4()
{
cout << "Derive::func4()" << endl;
}
private:
int d;
};
//1 > class Derive size(20) :
//1 > +-- -
//1 > 0 | {vfptr}
//1 > 4 | {vbptr}
//1 > 8 | d
//1 > +-- -
//1 > +-- - (virtual base Base)
//1 > 12 | {vfptr}
//1 > 16 | b
//1 > +-- -
//1 >
//1 > Derive::$vftable@Derive@:
//1 > | &Derive_meta
//1 > | 0
//1 > 0 | &Derive::fun3
//1 >
//1 > Derive::$vbtable@:
//1 > 0 | -4
//1 > 1 | 8 (Derived(Derive + 4)Base)
//1 >
//1 > Derive::$vftable@Base@:
//1 > | -12
//1 > 0 | &Derive::fun1
//1 > 1 | &Base::fun2
//1 >
//1 > Derive::fun1 this adjustor: 12
//1 > Derive::fun3 this adjustor: 0
//1 > vbi: class offset o.vbptr o.vbte fVtorDisp
//1 > Base 12 4 4 0
//1 >
多虚拟继承
class Base1
{
public:
virtual void fun1()
{
cout << "Base1::fun1" << endl;
}
virtual void fun2()
{
cout << "Base1::fun2" << endl;
}
private:
int b1;
};
class Base2
{
public:
virtual void fun1()
{
cout << "Base2::fun1" << endl;
}
virtual void fun2()
{
cout << "Base2::fun2" << endl;
}
private:
int b2;
};
class DeriveA : virtual public Base1, virtual public Base2
{
public:
virtual void fun1()
{
cout << "DeriveA::fun1" << endl;
}
virtual void fun3()
{
cout << "DeriveA::fun3" << endl;
}
private:
int d1;
};
//1 > class DeriveA size(28) :
//1 > +-- -
//1 > 0 | {vfptr}
//1 > 4 | {vbptr}
//1 > 8 | d1
//1 > +-- -
//1 > +-- - (virtual base Base1)
//1 > 12 | {vfptr}
//1 > 16 | b1
//1 > +-- -
//1 > +-- - (virtual base Base2)
//1 > 20 | {vfptr}
//1 > 24 | b2
//1 > +-- -
//1 >
//1 > DeriveA::$vftable@DeriveA@:
//1 > | &DeriveA_meta
//1 > | 0
//1 > 0 | &DeriveA::fun3
//1 >
//1 > DeriveA::$vbtable@:
//1 > 0 | -4
//1 > 1 | 8 (DeriveAd(DeriveA + 4)Base1)
//1 > 2 | 16 (DeriveAd(DeriveA + 4)Base2)
//1 >
//1 > DeriveA::$vftable@Base1@:
//1 > | -12
//1 > 0 | &DeriveA::fun1
//1 > 1 | &Base1::fun2
//1 >
//1 > DeriveA::$vftable@Base2@:
//1 > | -20
//1 > 0 | &thunk: this -= 8; goto DeriveA::fun1
//1 > 1 | &Base2::fun2
//1 >
//1 > DeriveA::fun1 this adjustor: 12
//1 > DeriveA::fun3 this adjustor: 0
//1 > vbi: class offset o.vbptr o.vbte fVtorDisp
//1 > Base1 12 4 4 0
//1 > Base2 20 4 8 0
//1 >
菱形虚拟继承
class Base
{
public:
virtual void func1()
{
cout << "Base::func1()" << endl;
}
virtual void func2()
{
cout << "Base::func2()" << endl;
}
private:
int b;
};
class Base1 : virtual public Base
{
public:
virtual void func1()
{
cout << "Base1::func1()" << endl;
}
virtual void func3()
{
cout << "Base1::func3()" << endl;
}
private:
int b1;
};
class Base2 :virtual public Base
{
public:
virtual void func1()
{
cout << "Base2::func2()" << endl;
}
virtual void func4()
{
cout << "Base2::func4()" << endl;
}
private:
int b2;
};
class Derive : virtual public Base1, virtual public Base2
{
public:
virtual void func1()
{
cout << "Derive::func1()" << endl;
}
virtual void func5()
{
cout << "Derive::func5()" << endl;
}
private:
int d;
};
//1 > class Derive size(44) :
//1 > +-- -
//1 > 0 | {vfptr}
//1 > 4 | {vbptr}
//1 > 8 | d
//1 > +-- -
//1 > +-- - (virtual base Base)
//1 > 12 | {vfptr}
//1 > 16 | b
//1 > +-- -
//1 > +-- - (virtual base Base1)
//1 > 20 | {vfptr}
//1 > 24 | {vbptr}
//1 > 28 | b1
//1 > +-- -
//1 > +-- - (virtual base Base2)
//1 > 32 | {vfptr}
//1 > 36 | {vbptr}
//1 > 40 | b2
//1 > +-- -
//1 >
//1 > Derive::$vftable@:
//1 > | &Derive_meta
//1 > | 0
//1 > 0 | &Derive::func5
//1 >
//1 > Derive::$vbtable@Derive@:
//1 > 0 | -4
//1 > 1 | 8 (Derived(Derive + 4)Base)
//1 > 2 | 16 (Derived(Derive + 4)Base1)
//1 > 3 | 28 (Derived(Derive + 4)Base2)
//1 >
//1 > Derive::$vftable@Base@:
//1 > | -12
//1 > 0 | &Derive::func1
//1 > 1 | &Base::func2
//1 >
//1 > Derive::$vftable@Base1@:
//1 > | -20
//1 > 0 | &Base1::func3
//1 >
//1 > Derive::$vbtable@Base1@:
//1 > 0 | -4
//1 > 1 | -12 (Derived(Base1 + 4)Base)
//1 >
//1 > Derive::$vftable@Base2@:
//1 > | -32
//1 > 0 | &Base2::func4
//1 >
//1 > Derive::$vbtable@Base2@:
//1 > 0 | -4
//1 > 1 | -24 (Derived(Base2 + 4)Base)
//1 >
//1 > Derive::func1 this adjustor: 12
//1 > Derive::func5 this adjustor: 0
//1 > vbi: class offset o.vbptr o.vbte fVtorDisp
//1 > Base 12 4 4 0
//1 > Base1 20 4 8 0
//1 > Base2 32 4 12 0
//1 >