c++——多态(补充)

优先查看:c++——多态_Hiland.的博客-CSDN博客


目录

菱形虚拟继承子类的重写问题

菱形虚拟继承中的偏移量补充

逆向思维——汇编查看多态中被重写的虚函数


菱形虚拟继承子类的重写问题

继承环节时,菱形虚拟继承解决了菱形继承的数据冗余和二义性问题。菱形虚拟继承在多态中使用会出现一些问题,有如下代码:

class A
{
public:
	virtual void func() {}
	int _a;
};
//继承A
class B : virtual public A
{
public:
	virtual void func() {}
    virtual void func1() {}
	int _b;
};
//继承A
class C : virtual public A
{
public:
	virtual void func() {}
	int _c;
};
//继承B和继承C
//当B和C都采用虚继承继承A,并且重写了A中的func虚函数,D继承了B和C,D必须重写A中的func虚函数
//否则D无法知道将B的虚函数写入虚表中还是将C的虚函数写入虚表中
class D : public B,  public C
{
public:
	//必须重写func
	//virtual void func() {}
	int _d;
};

int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	return 0;
}

运行如上代码,编译器报错,报错如下:

当B和C都采用虚继承继承A,并且重写了A中的func虚函数,D继承了B和C,D必须重写A中的func虚函数,否则D无法知道将B的虚函数写入虚表中还是将C的虚函数写入虚表中,因此D必须重写func函数。

菱形虚拟继承中的偏移量补充

(1条消息) c++——继承_Hiland.的博客-CSDN博客

将上述代码修改后运行并查看内存窗口里的偏移量时,之前发现偏移量上方还有一个00000000的数据,在多态时使用到了该数据。如下图:

此时由于B类增加了一个虚函数,所以B中有一个对应的虚表,根据内存窗口查看得知第二个就是之前在继承阶段每个类距离a的偏移量,查询b中的偏移量地址,发现如下图:

本该在继承阶段的那个00000000 变成了fcffffff,这个其实是该类到虚表的偏移量,所以该值是在多态中使用到的。

逆向思维——汇编查看多态中被重写的虚函数

当我们在vs下针对多继承问题想查看子类后面的虚函数时会使用以下代码:

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);//Base1类
    PrintVTable(vTableb1);
    VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));//Base2类
    PrintVTable(vTableb2);
    return 0;
}

我们已经查看了派生类的虚函数也进了虚表中,但是在vs的监视窗口中我们发现了基类和派生类的func1函数地址不同,在debug下查看监视窗口,出现如下图的情况:

同样是在d的派生类对象中,Base1部分的func1函数和Base2部分的func1函数地址不同,Base1部分func2函数和Base2部分的func2函数不同,在这里以func1函数为例探索,先在main函数中打印d对象中的func1函数地址看看:

对比打印虚表中的Base1部分的func1函数,虚表中的Base2部分的func1函数,以及直接打印d对象中func1函数,发现Base1和Base2中的函数地址不一样且和d对象中的func1函数地址都不一样

为了查看究竟是发生了什么,我们在vs下进行反汇编,通过汇编过程查看Base1和Base2中分别在调用func1的过程

通过反汇编查看的Base1中的func1函数的调用:

 

通过反汇编查看的Base2中的func1函数的调用:

通过以上的Base1和Base2两个类分别调用func1,能够得到以下的结论:

这里运用的就是逆向思维,通过汇编代码来查看一些编译器中出现的无法理解的现象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hiland.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值