之因此寫這篇文章,主要是爲了回答網友 zhancaihua123同窗的下面幾個問題:web
father* p=new son;
p->disp(...);
father是父類,son是子類。disp是一個子類重定義過的虛函數。
問題一:p->disp(...);是否是能夠寫成p->disp(p,...);
問題二:p的類型是否是father*
問題三:子類disp函數的this指針是否是son*類型的
問題四:若是第三的問題回答「是」請回答第四題,回答「不是」的,請回避!
那麼若是disp函數的this指針是 son*類型的,用p調用disp的時候,咱們將p的值傳遞給disp的this指針,那麼既然p是father*類型的,而this是son*類型的,則上述操做就至關於把父類的指針轉換爲子類指針,這不是與父類指針不能自動(隱式)轉換爲子類指針矛盾麼?
看回答,發現有好多人this指針的本質有錯誤的認識,估計很多人一提及this指針,腦殼當即反應出:那個類裏使用的this指針,那麼這個指針就是那個類類型咯。其實事實根本不是這樣子的,這裏修正對this指針的錯誤理解:函數
首先,zhancaihua123同窗,我在這裏重申一點:「this」不是變量,是關鍵字,意味着this指針並非哪一個真實存在的符號/儲存空間。因此,this指針沒有C++語言範疇裏的變量類型。因此我曾說的「子類disp函數的this指針是son*類型」這個說法是錯的。this指針即不能說是son*類型也不能說是"father"類型。this
固然,可能很多同窗所以反駁,我在IDE裏敲入「this->」後會自動彈出類的成員變量啊,怎麼就說this沒有類型呢?確實,this指針多多少少跟所在類的類型有關。所以,我也對this指針使用「類型」一詞,可是這裏「類型」包含下面的兩層含義:spa
由於在機器層面,變量(其實壓根就沒有什麼變量,該叫操做數)除了整型、浮點型之分外,是沒有類型的,更沒有類成員一說,只有尋址。據此,再進一步說明以前先介紹兩個基本常識:(一下所有在X86-64機器VC++編譯器範圍類討論)指針
======================================基本常識分界線=====================================code
基本常識1:orm
類成員是如何訪問的?(注:沒有特別說明,都不包括靜態成員)對象
當C++被編譯成二進制代碼後,不考慮什麼導出符號、未定義符號之類的,什麼變量名就通通消失,那麼要訪問這些變量,就得經過機器層面的方式:如以某個地址做爲標準/基準(起始地址),經過距離這個基準必定量來定位要訪問的變量。對於類成員來講,通常是對象首地址+偏移。好比如下這個類:blog
class A { int a; int b; virtual void F(); }
他的一個對象aa在內存中的分佈以下:(爲何起始地址爲F0H?沒有爲何,隨便定的)繼承
那麼假設對象aa的首地址(F0H)保存在寄存器rax中,那麼成員a的訪問方式爲:
rax+8h
同理,成員b的訪問方式:
rax+Ch
基本常識2:
什麼是繼承?
好比以下定義:
struct IA { int a; virtual void F(); void FF(); }; struct B { int b; virtual void G()=0; }; class Derived:public B,public IA { public: int d; virtual void F(); void FF(); virtual void G(); };
那麼假設有Derived d,d對象的內存分佈圖以下:
好了,簡介完兩個基本常識,開始講this指針的「類型」。
========================================this指針的類型=================================
this指針跟很多人想象的不同,它的類型由被調用函數決定。它的類型遵循着這兩點規則:(途中打勾爲首地址。)
1.對於非虛函數,this指針的基準地址爲函數定義所在層級對象的首地址,範圍爲該層級對象始末。
如:IA::FF();
this指針類型是以D0H爲首地址,範圍是從首地址開始到DFH爲止。(其實裏面有內存空洞,咱們不去糾結這個)
Derived::FF();
this指針類型是C0H爲首地址,範圍是從首地址到E8H爲止。
2.對於虛函數,this指針的基準地址爲函數首先聲明者的首地址,範圍爲實現者的始末。
如:IA::F();
其this指針類型是以D0H爲首地址,範圍是從首地址開始到DFH爲止。
Derived::F();
其this指針類型是以D0H爲首地址,範圍是從C0H到E8H爲止。
那麼當有IA* a=new Derived();後,
a->F();即是這麼訪問b成員的了。假設首地址D0H保存在寄存器rax裏,
rax-8
========================================結案陳詞======================================
好了,這裏能夠最後總結回答原問題三及其新問的「若是用對象指針調用函數,那麼究竟是指針傳遞給this仍是&object傳遞給this?」
之因此有 zhancaihua123同窗所疑惑的「this指針究竟是father*仍是son*」,祕密就在這裏。之因此以爲它像是father*,是由於形如IA* a=new Derived();a所存放的地址是D0H,經過a所能直接訪問的範圍限於上面的橙色區域,而且當調用F()時,所傳的地址、this指針的值就是a的值D0H。而又以爲它是son*,是由於在F()內部,經過this指針能夠訪問的範圍值整個子類對象。因而乎就讓人以爲有父類指針隱式轉換爲子類指針之嫌。
順便解答了 zhancaihua123同窗第二個問題,就是傳給this指針的確定是a的值即對象指針的值。
=========================================鳴謝========================================
有人說,提出好的問題,等於解決了問題的一半。有些問題,咱們沒留意到,沒認真想過,因此一般都是想固然。正是 zhancaihua123同窗所發問,引發個人思考,纔去寫代碼檢驗本身的想法。因此,最該感謝的,就是提出問題的zhancaihua123同窗。