美文网首页C++幕后故事
C++幕后故事(一)--this指针调整

C++幕后故事(一)--this指针调整

作者: xiong_cpp | 来源:发表于2019-10-27 15:08 被阅读0次

    读者如果觉得我文章还不错的,希望可以多多支持下我,文章可以转发,但是必须保留原出处和原作者署名。更多内容请关注我的微信公众号:cpp手艺人。

    image

    C++下继承了多个父类,子类是如何访问父类的属性?

    简单的说通过调整子类的this指针以实现访问各个父类的属性。

    1.什么叫this指针调整?

    在c++中多继承过程,根据访问不同的父类成员变量或者是成员函数,同一个实例对象会出现不同的基址(对象的地址,类似于你在不同的场合就会有身份的转换,在家的身份,在学校,在公司的等等),这种现象叫做this指针基址调整。

    2.如何调整?

    1.如果继承的顺序是A,再B,那么初始化时先A再B,内存布局如下图1所示。


    image

    2.如果继承的顺序还是是A,再B,那么初始化时的顺序不变,还是先A再B,但是B存在virtual function,内存布局如下图2所示。

    3.如果继承的顺序是A,再B,初始化时还是先A再B,但是A,B同时存在virtual function,内存布局还是如图1所示,没有变化。

    3.思考:

    1.那具体又是怎么调整的?

    比如说,继承A,又继承B(先不考虑含有virtual fucntion情况)。为了获取B的成员变量。这是this指针需要偏移字节数为sizeof(A)。从图1上来看,要先越过A所占用的大小。

    2.为什么父类中virtual function,内存布局发生了变化?

    因为有了virtual function,类中就需要产生指向一个虚函数表的指针。而虚函数表指针是埋在对象的首地址中。所以B有virtual function,那么B的普通成员变量将会偏移到A的前面。

    3.那么既然内存布局发生了变化,但是我们发现只要继承的顺序不变,那么构造的先后顺序就不会发生变化。说明构造的先后顺序和内存布局没有任何的关系。

    4.既然我们知道了这个this指针的调整,但是有什么用呢?

    A.通过指针位置的偏移不就可以访问了原来编译器层面阻止你不想访问的私有属性。
    B.这种解决了多个父类下访问父类的属性的解决方案,和多态的方案是非常的相似。
    

    如果上面说的你还是一知半解的,我再举个例子说明。

    假设在一个家庭中,有你和你爸、你妈。你继承了你爸你妈的所有的财产。

    1.假如,你爸在家里占据中主导地位(类似于你爸包含有虚函数)。那么在你的心目中你爸就在第一位的。

    2.假如,你妈在家里占据中主导地位(类似于你妈包含有虚函数)。那么在你的心目中你妈就在第一位的。

    3.假如,你爸和你妈在家平分秋色(都包含虚函数或者都不包含虚函数)。那么在你的心目中你爸和你妈,你和谁离得近(继承的顺序,谁先继承),谁就在第一位的。

    4.但是不管是谁在第一位,你家的户口本上你爸肯定是在第一页的(构造函数的顺序不变)。

    5.假如,晚上你们一家三口在床上睡觉,你,你爸,你妈这样的顺序。但是假如你想和你妈睡在一起。你是不是要翻过你爸这座大山(类的大小),才能和你妈睡在一起。(this指针如何调整的)。

    6.假如,你想要零花钱,你有两种方式,一种向你妈要,一种是向你爸要。不管是向谁要,是不是都要先找对对象,才能拿到零花钱。在多继承中子类有多个父类,所以访问某个对象的属性(成员变量,函数),则需要先找到基类的地址,为了找到正确的对象都需要进行地址偏移,这就好像你是找你爸还是找你妈要零花钱都必须先找到这个人。这就是为什么需要调整this指针。

    4.代码实例:

    /****************************************************************************
    **
    ** Copyright (C) 2019 635672377@qq.com
    ** All rights reserved.
    **
    ****************************************************************************/
    
    #ifndef objanalyze_h
    #define objanalyze_h
    
    #include <iostream>
    
    using std::cout;
    using std::endl;
    
    #define VIRTUAL_PARENT_A
    #define VIRTUAL_PARENT_B
    
    namespace ObjAnalyze
    {
    class ParentA
    {
    public:
        ParentA()
        {
            cout << "parent0A address "  << this << endl;
        }
    
    #ifdef VIRTUAL_PARENT_A
        virtual ~ParentA()
        {}
    #endif // VIRTUAL_PARENT_A
    
        void Func0A()
        {
            cout << "parent0A Func0A, address "  << this << endl;
        }
    
    private:
        int m_age;
        int m_high;
    
    };
    
    class ParentB
    {
    public:
        ParentB()
        {
            cout << "parent0B address "  << this << endl;
        }
    
    #ifdef VIRTUAL_PARENT_B
        virtual ~ParentB()
        {}
    #endif //  VIRTUAL_PARENT_B
    
        void Func0B()
        {
            cout << "parent0B Func0B, address "  << this << endl;
        }
    
    private:
        int m_grade;
        int m_class;
    
    };
    
    class Child : public ParentA, public ParentB
    {
    public:
        Child()
        {
            cout << "child address "  << this << endl;
        }
    
        void Func0A()
        {
            cout << "child Func0A "  << this << endl;
        }
    
    };
    
    void test_this_point_address()
    {
        cout << "ParentA size " << sizeof(ParentA) << endl;
        cout << "ParentB size " << sizeof(ParentB) << endl;
        cout << "Child size " << sizeof(Child) << endl;
    
        Child child;
        child.Func0A();
        child.ParentA::Func0A();
        child.Func0B();
        system("pause");}
    }
    
    #endif // objanalyze_h
    
    1.parentA,B都没有virtual function
    // ParentA size 8
    // ParentB size 8
    // Child size 16
    // parent0A address 00AFFD78
    // parent0B address 00AFFD80
    // child address 00AFFD78
    // child Func0A 00AFFD78
    // parent0A Func0A, address 00AFFD78
    // parent0B Func0B, address 00AFFD80
    2.parentA含有virtual function,B没有
    // ParentA size 12
    // ParentB size 8
    // Child size 20
    // parent0A address 00EFF7B8
    // parent0B address 00EFF7C4
    // child address 00EFF7B8
    // child Func0A 00EFF7B8
    // parent0A Func0A, address 00EFF7B8
    // parent0B Func0B, address 00EFF7C4
    3.parentB含有virtual function,A没有
    // ParentA size 8
    // ParentB size 12
    // Child size 20
    // parent0A address 004FF700
    // parent0B address 004FF6F4
    // child address 004FF6F4
    // child Func0A 004FF6F4
    // parent0A Func0A, address 004FF700
    // parent0B Func0B, address 004FF6F4
    4.parentA,B都含有virtual function
    // ParentA size 12
    // ParentB size 12
    // Child size 24
    // parent0A address 0077F870
    // parent0B address 0077F87C
    // child address 0077F870
    // child Func0A 0077F870
    // parent0A Func0A, address 0077F870
    // parent0B Func0B, address 0077F87C
    OptimizationA oe;
    OptimizationA of(oe);
    OptimizationA og = oe;
    OptimizationA oh = OptimizationA(oe); 
    // 编译器的角度看,分成两步走
    // 1.OptimizationA of (注意此时不会调用OptimizationA的默认构造函数)
    // 2.of.OptimizationA::OptimizationA(oe) (调用拷贝构造函数)
    // 3.og.OptimizationA::OptimizationA(oe) (调用拷贝构造函数)
    // 4.oh.OptimizationA::OptimizationA(oe) (调用拷贝构造函数)
    

    相关文章

      网友评论

        本文标题:C++幕后故事(一)--this指针调整

        本文链接:https://www.haomeiwen.com/subject/swyopxtx.html