C 幕後故事(一)--this指針調整

1.什麼叫this指針調整?

在c++中多繼承過程,根據訪問不同的父類成員變量或者是成員函數,同一個實例對象會出現不同的基址(對象的地址,類似於你在不同的場合就會有身份的轉換,在家的身份,在學校,在公司的等等),這種現象叫做this指針基址調整。

2.如何調整?

1.如果繼承的順序是A,再B,那麼初始化時先A再B,內存佈局如下圖1所示。

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 [email protected]
** 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

打開或者關閉上面的#define VIRTUAL_PARENT_A,#define VIRTUAL_PARENT_B宏,四種結果如下。

    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) (調用拷貝構造函數)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章