我们大部分时候使用的多态虚表实在public继承的基础上实现的
这句话可以怎么说了?
自己简单的思考逻辑认为基类中的成员或者成员函数子类中都会重新拷贝一份,也就是如果派生类中没有定义基类的某虚函数重写但是这个**虚函数的地址已经被写实拷贝写了一份放到派生类·虚表当中了**
- 好了,来看一下一种情况下的虚表情况吧!
- 单继承虚表
class Base
{
public:
virtual void func1()
{
cout << "Base::func1" << endl;
}
virtual void func2()
{
cout << "Base::func2" << endl;
}
private:
int a;
};
class Derive :public Base
{
public:
virtual void func1()
{
cout << "Derive::func1" << endl;
}
virtual void func3()
{
cout << "Derive::func3" << endl;
}
virtual void func4()
{
cout << "Derive::func4" << endl;
}
private:
int b;
};
int main()
{
Base b;
Derive d;
return 0;
}
- !!!看一下这里问题就出来了,和理论之中存在差别,基类子类当中的自己虚函数并没有显示出来
- 这里是编译器的监视窗口故意隐藏了这两个函数, 也可以认为是他的一个小bug。那么我们如何查看d的虚表呢?下面我们使用代码打印出虚表中的函数。
模拟定义一个虚表指针,来把虚表元素打印出来
class Base
{
public:
virtual void func1()
{
cout << "Base::func1" << endl;
}
virtual void func2()
{
cout << "Base::func2" << endl;
}
private:
int a;
};
class Derive :public Base
{
public:
virtual void func1()
{
cout << "Derive::func1" << endl;
}
virtual void func3()
{
cout << "Derive::func3" << endl;
}
virtual void func4()
{
cout << "Derive::func4" << endl;
}
private:
int b;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
cout << " 虚表地址>" << vTable << endl;
for (int i = 0; vTable[i] != nullptr; ++i)
{
printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
VFPTR f = vTable[i];
f();
}
cout << endl;
}
int main()
{
Base b;
Derive d;
VFPTR* vTableb = (VFPTR*)(*(int*)&b);
PrintVTable(vTableb);
VFPTR* vTabled = (VFPTR*)(*(int*)&d);
PrintVTable(vTabled);
return 0;
}
- 思路:取出b、d对象的头4bytes,就是虚表的指针,前面我们说了虚函数表本质是一个存虚函数指针的 指针数组,这个数组最后面放了一个nullptr
- 1.先取b的地址,强转成一个int*的指针
- 2.再解引用取值,就取到了b对象头4bytes的值,这个值就是指向虚表的指针
- 3.再强转成VFPTR*,因为虚表就是一个存VFPTR类型(虚函数指针类型)的数组。
- 4.虚表指针传递给PrintVTable进行打印虚表
- 5.需要说明的是这个打印虚表的代码经常会崩溃,因为编译器有时对虚表的处理不干净,虚表最后面没有 放nullptr,导致越界,这是编译器的问题。我们只需要点目录栏的-生成-清理解决方案,再编译就好了。
- 多继承虚表
class Base1
{
public:
virtual void func1()
{
cout << "Base1::func1" << endl; }
virtual void func2()
{
cout << "Base1::func2" << endl;
}
private:
int b1;
};
class Base2
{
public:
virtual void func1()
{
cout << "Base2::func1" << endl;
}
virtual void func2()
{
cout << "Base2::func2" << endl;
}
private:
int b2;
};
class Derive : public Base1, public Base2
{
public:
virtual void func1()
{
cout << "Derive::func1" << endl;
}
virtual void func3() {
cout << "Derive::func3" << endl;
}
private: int d1;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[]) {
cout << " 虚表地址>" << vTable << endl;
for (int i = 0; vTable[i] != nullptr; ++i)
{
printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
VFPTR f = vTable[i];
f();
}
cout << endl;
}
int main() {
Derive d;
VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
PrintVTable(vTableb1);
VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));
PrintVTable(vTableb2);
return 0;
}
可以看出:多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中