c++多继承下,多态调用的理解

以下内容在初步了解c++多态的知识后再阅读更佳。
案例源码:

class Base1 {
public:
    Base1() {
        b1 = 111;
    }
public:
    virtual void fun1() {
        cout << "Base1:fun1" << b1 << " " << this << endl;
    }
    virtual void fun2() {
        cout << "Base1:fun2" << b1<<" " << this << endl;
    }
protected:
    int b1;
    int a[100];
};

class Base2 {
public:
    Base2() {
        b2 = 222;
    }
    virtual void fun1() {
        cout << "Base2:fun1" << b2<<" "<<this << endl;
    }
    virtual void fun2() {
        cout << "Base2:fun2" << b2 <<" "<< this << endl;
    }
    int b2;
};

class Derive1 :public Base1, public Base2 {
public:
    Derive1() {
        d1 = 123;
    };
    void nfun1() {
        cout << "Derive1:nfun1" << endl;
    }

    virtual void fun1() {
        cout << "Derive1:fun1" <<" "<<this<<" "<< d1 << endl;
    }
    virtual void fun3() {
        cout << "Derive1:fun3" << " " << this << " " << endl;
    }
    int d1;
};

int main()
{
    int a;
    Derive1 d1;
    d1.nfun1();
    void* pd1 = &d1;
    Base2* pB = (Base2 *)pd1;
    Base2* pB2 = &d1;
    cout << pB << " " << pB2 << " "<< &d1<< endl;
	//提问:以下四个基类指针调用的函数输出是什么?
    pB->fun2();
    pB2->fun2();
    pB->fun1();
    pB2->fun1();
}

对于源码中注释的问题:
最开始我以为pB->fun2和pB2->fun2都是Base2::fun2;pB->fun1和pB2->fun1则都是Derive1::fun1。
但实际的输出结果是:
Derive1:nfun1
0000008CCB72F910 0000008CCB72FAB0 0000008CCB72F910
Base1:fun2111 0000008CCB72F910
Base2:fun2222 0000008CCB72FAB0
Derive1:fun1 0000008CCB72F910 123
Derive1:fun1 0000008CCB72F910 123

这里我最开始有一个问题:
为什么Base2的指针调用的函数 pB->fun2() 的结果是Base2::fun2 ?

分析:
在多继承中,Derive1对象的内存分布是这样的(VS的监视窗口)
在这里插入图片描述

按照继承顺序,内存分布如下:

  1. Derive1:Base1的虚函数指针
  2. Base1 非static成员变量
  3. Derive1:Base2的虚函数指针
  4. Base2 非static成员变量

在上面的例子中,由于pB是通过强转赋值d1的地址,pB在调用虚函数时,用到的虚函数指针就会是Derive1:Base1的虚函数指针,所以调用了Base1::fun2虚函数。而pB2被编译器赋值d1+sizeof(Base1),这个地址实际上是Derive1:Base2虚函数的指针的地址,所以调用了Base2::fun2。

这个结果理解之后,我又有一个问题了:
既然pB和pB2不一样,为什么调用的fun1打印的this指针是一样的?(按我的理解应该分别打印pB和pB2的地址)
细心的同学可能在上面那张VS的监控窗口中发现了一个点:循环表中代替Base2::fun1的函数不是Derive1::fun1而是Derive1::fun1`adjustor{416}`,到这里就可以猜测这个函数把this从原本的&d1+sizeof(Base1)调整回&d1之后再调用真正的Derive1::fun1。

为了验证猜测,我们在pB2->fun1断点后打开反汇编(VS的调试->窗口->反汇编)。
在这里插入图片描述

一般this是通过rcx寄存器来传递给非static成员函数的, 见上图红框。

rax寄存器则存放了虚函数Derive1::fun1`adjustor{416}`地址,不方便看,我们直接从监视窗口拷贝Derive1::fun1`adjustor{416}\ 的地址 0x00007ff76fa21113,在反汇编中跳转至该汇编地址,步进到某步可以看到:
在这里插入图片描述

这里对rcx减了一个值1A0h(416), 即把this减了1A0h 变成了 d1的地址,然后才调用的真正的fun1,验证了之前的猜测。

总结:在使用 c++的多继承配合多态时,应该谨慎一些,避免强转指针导致运行出现错误的行为,甚至可以少使用多继承,考虑用组合方式代替。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值