多重继承的虚函数表与类型转换
总结:
- 非virtual继承:
进行隐式转换或进行显式转换均可时(代码中简写为隐式强转),最左父类指针指向子类时指向地址不变,其他父类指针指向子类时指向地址需要偏移,根据指针的类型对虚函数进行地址偏移(访问指针类型对象的虚函数表)。
必须进行显式转换时指针指向地址均不变,按照强制转换前的类型对虚函数进行地址偏移(访问原来类型的虚函数表)。
但是它们的实际函数都是访问子类的实际虚函数。
- virtual继承:
进行隐式转换或进行显式转换均可时(代码中简写为隐式强转),指针指向子类时指向地址均需要偏移,根据指针的类型对虚函数进行地址偏移(访问指针类型对象的虚函数表)。
必须进行显式转换时指针指向地址均不变,按照强制转换前的类型对虚函数进行地址偏移(访问原来类型的虚函数表)。
但是它们的实际函数都是访问子类的实际虚函数。
virtual继承与非virtual继承的差别其实仅在于不需要显式转换时的最左父类指针是否偏移。这与当时的虚函数表布局方式有关。
virtual继承将新的virtual方法都写在自身vftable中; 而非virtual继承则将新的virtual方法更新到最左父类的vftable中。
-
#include <iostream>
-
-
class A
-
{
-
public:
-
void fun() { std::cout << "A::fun()" << std::endl; }
-
virtual void vfun() { std::cout << "A::vfun()" << std::endl; }
-
virtual void vfun1() { std::cout << "A::vfun1()" << std::endl; }
-
};
-
-
class B
-
{
-
public:
-
virtual void vfun2() { std::cout << "B::vfun2()" << std::endl; }
-
};
-
-
class C
-
{
-
public:
-
virtual void vfun3() { std::cout << "C::vfun3()" << std::endl; }
-
};
-
-
class D: public A, public B, public C
-
{
-
public:
-
virtual void vfun2() { std::cout << "D::vfun2()" << std::endl; }
-
virtual void vfun3() { std::cout << "D::vfun3()" << std::endl; }
-
virtual void vfun() { std::cout << "D::vfun()" << std::endl; }
-
};
-
-
typedef void (*Fun) (void);
-
-
void TestCast(D* d)
-
{
-
std::cout << "d: \t\t\t" << d << std::endl << std::endl;
-
-
A* pA = d;
-
std::cout << "A* pA = d: \t\t" << pA << std::endl;
-
std::cout << "pA -> fun(): \t\t"; pA->fun();
-
std::cout << "pA -> vfun(): \t\t"; pA->vfun();
-
std::cout << "pA -> vfun1(): \t\t"; pA->vfun1();
-
std::cout << "隐式强转:最左父类的指针指向地址不变,指向函数按指针类型对象的虚表结构偏移,但虚表依然是原对象的虚表" << std::endl;
-
std::cout << std::endl;
-
-
C* pC = (C*) pA;
-
std::cout << "C* pC = (C*) pA: \t" << pC << std::endl;
-
std::cout << "pC -> vfun3(): \t\t"; pC->vfun3();
-
std::cout << "显式强转:指针指向地址不变,指向函数按强转前的类型的虚表结构偏移,但虚表依然是原对象的虚表" << std::endl;
-
std::cout << std::endl;
-
-
B* pB = d;
-
std::cout << "B* pB = d: \t\t" << pB << std::endl;
-
std::cout << "pB -> vfun2(): \t\t"; pB->vfun2();
-
std::cout << "隐式强转:非最左父类的指针指向地址变化,指向函数按指针类型对象的虚表结构偏移,但虚表依然是原对象的虚表" << std::endl;
-
std::cout << std::endl;
-
-
pA = (A*) pB;
-
std::cout << "A* pA = (A*) pB: \t" << pA << std::endl;
-
std::cout << "pA -> fun(): \t\t"; pA->fun();
-
std::cout << "pA -> vfun(): \t\t"; pA->vfun();
-
std::cout << "显式强转:指针指向地址不变,指向函数按强转前的类型的虚表结构偏移,但虚表依然是原对象的虚表" << std::endl;
-
//pA->vfun1(); // B类中只有一个virtual
-
std::cout << std::endl;
-
}
-
-
void TestCast(A* a)
-
{
-
std::cout << "a: \t\t\t" << a << std::endl << std::endl;
-
-
D* d = (D*) a;
-
std::cout << "D* d = a: \t\t" << d << std::endl;
-
d->fun();
-
d->vfun();
-
d->vfun1();
-
//d->vfun2(); // A类中没有vfun2
-
//d->vfun3(); // A类中没有vfun3
-
std::cout << std::endl;
-
-
B* b = d;
-
std::cout << "B* b = d: \t\t" << b << std::endl;
-
//b->vfun2(); // A类中没有vfun2
-
std::cout << std::endl;
-
-
b = (B*) a;
-
std::cout << "B* b = (B*) a:\t\t" << b << std::endl;
-
b->vfun2(); // A类vtable的第一个项是vfun
-
std::cout << std::endl;
-
}
-
-
void main(void)
-
{
-
D d;
-
TestCast(&d);
-
-
A a;
-
TestCast(&a);
-
-
system("pause");
-
}
virtual继承:
-
#include <iostream>
-
-
class A
-
{
-
public:
-
void fun() { std::cout << "A::fun()" << std::endl; }
-
virtual void vfun() { std::cout << "A::vfun()" << std::endl; }
-
virtual void vfun1() { std::cout << "A::vfun1()" << std::endl; }
-
};
-
-
class B
-
{
-
public:
-
virtual void vfun2() { std::cout << "B::vfun2()" << std::endl; }
-
};
-
-
class C
-
{
-
public:
-
virtual void vfun3() { std::cout << "C::vfun3()" << std::endl; }
-
};
-
-
class D: virtual public A, virtual public B, virtual public C
-
{
-
public:
-
virtual void vfun3() { std::cout << "D::vfun3()" << std::endl; }
-
virtual void vfun() { std::cout << "D::vfun()" << std::endl; }
-
};
-
-
void TestCast(D* d)
-
{
-
std::cout << "d: \t\t\t" << d << std::endl << std::endl;
-
-
A* pA = d;
-
std::cout << "A* pA = d: \t\t" << pA << std::endl;
-
std::cout << "pA -> fun(): \t\t"; pA->fun();
-
std::cout << "pA -> vfun(): \t\t"; pA->vfun();
-
std::cout << "pA -> vfun1(): \t\t"; pA->vfun1();
-
std::cout << "隐式强转:指针指向地址变化,指向函数按指针类型对象的虚表结构偏移,但虚表依然是原对象的虚表" << std::endl;
-
std::cout << std::endl;
-
-
C* pC = (C*) pA;
-
std::cout << "C* pC = (C*) pA: \t" << pC << std::endl;
-
std::cout << "pC -> vfun3(): \t\t"; pC->vfun3();
-
std::cout << "显式强转:指针指向地址不变,指向函数按强转前的类型的虚表结构偏移,但虚表依然是原对象的虚表" << std::endl;
-
std::cout << std::endl;
-
-
B* pB = d;
-
std::cout << "B* pB = d: \t\t" << pB << std::endl;
-
std::cout << "pB -> vfun2(): \t\t"; pB->vfun2();
-
std::cout << "隐式强转:指针指向地址变化,指向函数按指针类型对象的虚表结构偏移,但虚表依然是原对象的虚表" << std::endl;
-
std::cout << std::endl;
-
-
pA = (A*) pB;
-
std::cout << "A* pA = (A*) pB: \t" << pA << std::endl;
-
std::cout << "pA -> fun(): \t\t"; pA->fun();
-
std::cout << "pA -> vfun(): \t\t"; pA->vfun();
-
std::cout << "显式强转:指针指向地址不变,指向函数按强转前的类型的虚表结构偏移,但虚表依然是原对象的虚表" << std::endl;
-
//pA->vfun1(); // B类中只有一个virtual
-
std::cout << std::endl;
-
}
-
-
void TestCast(A* a)
-
{
-
std::cout << "a: \t\t\t" << a << std::endl << std::endl;
-
-
//D* d = (D*) a; // 不能从虚拟基类转换成子类
-
-
B* b = (B*) a;
-
std::cout << "B* b = (B*) a:\t\t" << b << std::endl;
-
b->vfun2(); // A类vtable的第一个项是vfun
-
std::cout << std::endl;
-
}
-
-
void main(void)
-
{
-
D d;
-
TestCast(&d);
-
-
A a;
-
TestCast(&a);
-
-
system("pause");
-
}