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

讀者若是以爲我文章還不錯的,但願能夠多多支持下我,文章能夠隨便轉發,保留原出處就能夠了。或者關注個人微信公衆號:cpp手藝人。ios

C++下繼承了多個父類,子類是如何訪問父類的屬性?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所示,沒有變化。ui

3.思考:

1.那具體又是怎麼調整的?this

好比說,繼承A,又繼承B(先不考慮含有virtual fucntion狀況)。爲了獲取B的成員變量。這是this指針須要偏移字節數爲sizeof(A)。從圖1上來看,要先越過A所佔用的大小。spa

2.爲何父類中virtual function,內存佈局發生了變化?指針

由於有了virtual function,類中就須要產生指向一個虛函數表的指針。而虛函數表指針是埋在對象的首地址中。因此B有virtual function,那麼B的普通成員變量將會偏移到A的前面。code

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) (調用拷貝構造函數)
複製代碼
相關文章
相關標籤/搜索