人生几何?

多重继承成员布局与this指针的调整

2021-09-01  本文已影响0人  404Not_Found

父类无虚函数,子类有虚函数

class Base
{
public:
    int m_bi;
    Base() {
        printf("Base::Base()的this 指针是 %p\n", this);
    }
};
class Myclass : public Base
{
public:
    int m_i;
    int m_j;
    virtual void myvirfunc()
    {}
    Myclass()
    {
        int abc = 1;
        printf("Myclass::Myclass()的this 指针是 %p\n", this);
    }
    ~Myclass()
    {
        int def = 0;
    }
};

int main(int argc, char **argv)
{
    printf("myclass:m_bi = %d\n", &Myclass::m_bi);
    printf("myclass:m_i = %d\n", &Myclass::m_i);
    printf("myclass:m_j = %d\n", &Myclass::m_j);

    Myclass myobj;
    printf("myobj的地址是:%p\n", &myobj);
    myobj.m_bi = 0;
    myobj.m_i = 1;
    myobj.m_i = 2;

    return 0;
}
this指针位置不同.png

上述布局的输出结果很明显,如下图所示:


布局.png

子类有虚函数,父类也有虚函数

class Base
{
public:
    int m_bi;
    virtual void voidfatherfun()
    {

    }
    Base() {
        printf("Base::Base()的this 指针是 %p\n", this);
    }
};
class Myclass : public Base
{
public:
    int m_i;
    int m_j;
    virtual void myvirfunc()
    {}
    Myclass()
    {
        int abc = 1;
        printf("Myclass::Myclass()的this 指针是 %p\n", this);
    }
    ~Myclass()
    {
        int def = 0;
    }
};

int main(int argc, char **argv)
{
    printf("myclass:m_bi = %d\n", &Myclass::m_bi);
    printf("myclass:m_i = %d\n", &Myclass::m_i);
    printf("myclass:m_j = %d\n", &Myclass::m_j);

    Myclass myobj;
    printf("myobj的地址是:%p\n", &myobj);
    myobj.m_bi = 0;
    myobj.m_i = 1;
    myobj.m_i = 2;

    return 0;
图片.png

多重继承且父类都带虚函数的数据成员布局

class Base1
{
public:
    int m_b1i;
    virtual void voidfatherfun()
    {

    }
    Base1() {
        printf("Base1::Base1()的this 指针是 %p\n", this);
    }
};

class Base2
{
public:
    int m_b2i;
    virtual void voidfatherfun2()
    {

    }
    Base2() {
        printf("Base2::Base2()的this 指针是 %p\n", this);
    }
};
class Myclass : public Base1, public Base2
{
public:
    int m_i;
    int m_j;
    virtual void myvirfunc()
    {}
    Myclass()
    {
        int abc = 1;
        printf("Myclass::Myclass()的this 指针是 %p\n", this);
    }
    ~Myclass()
    {
        int def = 0;
    }
};

int main(int argc, char **argv)
{
    printf("sizeof Myclass:%d\n", sizeof(Myclass));
    printf("myclass:m_bi = %d\n", &Myclass::m_b1i);
    printf("myclass:m_bi = %d\n", &Myclass::m_b2i);
    printf("myclass:m_i = %d\n", &Myclass::m_i);
    printf("myclass:m_j = %d\n", &Myclass::m_j);

    Myclass myobj;
    printf("myobj的地址是:%p\n", &myobj);

    return 0;
}
运行结果.png

B1 和 Myclass 共用一个 this 指针, 如果访问B2 则需要修改this 指针的值。

内存布局如下:


内存布局.png

注意 this 指针的位置是不同的!

结论:
我们要访问一个类对象中的成员,成员定位是通过this指针的来的(编译器会自动调整,比如我们讨论的多重继承中Base1, Base2 就不用) 以及偏移值来定位的。
this指针的调整需要编译器来介入。

深解多重继承中的this偏移

Myclass myobj;
myobj.m_i = 1;
myobj.m_j = 2;
myobj.m_b1i = 3;
myobj.m_b2i = 4;

Base2 * pBase2 = &myobj;

这里的现象很有意思
&myobj 的地址 :
0x00bdfe04 {m_i=1 m_j=2 },
pbase2 的地址:
pbase2 = 0x00bdfe0c

两者并不相同,差了8个字节。
实际编译器是这么操作的:

    Base2 * pbase2 = ( ( (char*)&myobj) + sizeof(Base1) );

这里做的,个人认为是调整了this指针的位置。

同理

Base1 * pbase1 = &myobj 就不会进行调整
    Base2 *pBase3 = new Myclass(); //指向的是 Base2 的 this 指针位置 428
    Myclass *psubobj = (Myclass*)pBase3;// 420

分配堆后,进行转化,发现this 指针 同样调整了8个字节。多继承完全阐述了 编译器堆this指针的调整。

    delete psubobj; // 正确的
    //delete pBase3;// 分配的有问题,说明pBase3 并不是 真个堆的首地址。而是调整了this指针后的地址

下面的 delete pBase3 是绝对有问题的。因为delete不干净。会报错呢。

再次举例:


多重继承的举例.png

学到: 多重继承中,this指针的调整

上一篇下一篇

猜你喜欢

热点阅读