針對基類引用符指向派生類物件引起的思考

2021-06-06 09:20:51 字數 2783 閱讀 6759

針對《扣響c#之門》書中第九章中引出的虛方法繼承使用,產生了很多疑問(感謝該書能激發讀者的深思,剛開始學其他書時想都不會去想這些問題),關於這部分內容的確值得深入,先對基類引用符指向派生類物件引起的思考進行分析:

1、當派生類繼承基類時,實際上是將基類所有成員全部繼承下來(除了sealed宣告的密封函式或密封類),當建立派生類物件時,不論派生類是否重寫或隱藏了基類的成員,原基類的這些成員依然會被生成於託管堆的gc heap堆中(而且這些成員應該在記憶體中寫在派生類成員前面的位置),只是派生類物件本身不再訪問那些基類中私有的成員或者被派生類重寫或隱藏的成員。

2、在出現 宣告基類引用符,並將派生類物件的指標賦給它時,如animal ani1=new cat();  首先因為建立的是派生類的物件,當用 ani1.gettype()函式時,返回的是cat型別,原因是該函式是針對物件的!物件的實際型別當然是派生型別了。

但,引用符被宣告時,針對引用符訪問的區域卻未必相同(這應該也是多型性的體現吧),不同型別宣告的引用符在虛擬方法表中(即託管堆中的loader heap堆)只能訪問特定的區域(這個區域是由物件在gc heap堆中的type handle型別控制代碼指向的),在gc heap堆中堆成員變數的訪問也是只能訪問特定的區域。具體闡述如下:

1)所有的變數和成員的訪問(不論值型別還是引用型別)都是在乙個區域內由高位址向低位址進行訪問(這樣說不知道合理否?)。

a、值型別時,棧的分配是由高位址向低位址擴充套件,所以變數作用域才會有此規律:程式中前面的變數作用域最大,因為先進棧,放在了高位址處。

b、引用型別時,引用符是存放在棧中的,符合filo原則,但其指向的物件在堆中存放是由低位址向高位址擴充套件。因此,當宣告乙個派生類的物件時,基類的成員變數和成員函式都放在派生類的成員前面,也就是放在低位址。

正常的派生類引用符指向派生類物件,派生類引用符將獲得派生類及其所有基類的所在區域訪問許可權,因為派生類成員存放的位址高於基類的存放位址。不論派生類是否重寫或隱藏了基類的函式,根據「執行就近原則」,派生類引用符只會去訪問離其開始訪問最近的那個方法。

而基類引用符指向基類物件,基類引用符是不可能擁有其派生類的區域訪問許可權的,一則根本就沒有分配該部分記憶體,二則即使有,位址也會比基類成員高,基類引用符訪問不到。

2)對於宣告基類引用符,並將派生類物件的指標賦給它時,應針對以下情況進行討論

a、沒有派生類進行虛函式重寫或隱藏基類方法時,基類引用符雖指向物件,但被分配的訪問許可權卻被僅僅定格在堆中那些基類所擁有的成員上。

b、如果派生類進行了虛函式重寫,基類引用符訪問許可權將擴充套件到最後乙個重寫其虛函式的派生類所對應的重寫方法中。即此時的基類引用符訪問方法表中的位址已經提高到派生類重寫其虛函式的位址處,根據「執行就近原則」,基類引用符當然訪問被重寫後的同名方法了。

c、如果派生類對基類的某個成員進行了隱藏,且此成員為成員變數或者此成員不是虛函式,則訪問許可權和1)中所述一樣。

d、如果派生類對基類的虛函式沒有重寫,且對其進行了隱藏,則基類引用符訪問許可權和2)中所述一樣,我們也可稱此時對虛方法的隱藏(new),是new關鍵字在虛方法繼承中的阻斷作用。

因此,既然是基類宣告的,那麼該引用符只能訪問基類的對應成員,因為不論是成員變數還是成員方法都在不同的堆中,排在派生類的成員前面,就近原則就是也就是說雖然建立的是派生類物件,但由於宣告的是基類的引用符,該引用符只能訪問基類內的成員變數和成員函式,對於那些被重寫的虛方法(注意是那些沒有用new隱藏的方法),訪問基類成員方法許可權將擴充套件成訪問派生類中重寫後的方法。

試驗程式如下:

using system;

using system.collections.generic;

namespace test

set

}public virtual void displayname()

}public class

vertebrata:animal

set}

public override void displayname()//對方法displayname()進行了覆寫

}public class

mammal:vertebrata

set}

public override void displayname()

}public class

cat:mammal

set}

}public class

program}}

輸出:

輸出的第1到第3行,不用贅述了。

第四行,display()方法一直重寫到mammal類,所以當animal型別的引用符指向cat類的物件時,引用符所訪問的display()方法實際上是mammal類重寫後的方法。但this依然是代表真正的物件,所以才會有如此的輸出。

當mammal類中的display()前加new,也就是隱藏display()方法時,發生了new關鍵字在虛方法繼承中的阻斷作用,即animal類中的虛函式display()在mammal類中繼承中斷了,只在vertebrata類中完成了繼承並重寫,所以animal類引用符也只能訪問到vertebrata類中重寫的虛函式了。

輸出如下:

第4、5、9、10行的輸出明顯發生了變化。這些現象正好可以用上面分析論述進行解釋。

分類: 

c#學習筆記

基類指標指向派生類物件

成員函式一般化三個結論 include using std cout using std endl class employee class dev public employee int main 1.如果以 基類指標 指向 派生類物件 那麼經由該指標只能呼叫基類所定義的函式 2.如果以 派生類指...

基類指標指向派生類物件

父類子類指標函式呼叫注意事項 1,如果以乙個基礎類指標指向乙個衍生類物件 派生類物件 那麼經由該指標只能訪問基礎類定義的函式 靜態聯翩 2,如果以乙個衍生類指標指向乙個基礎類物件,必須先做強制轉型動作 explicit cast 這種做法很危險,也不符合生活習慣,在程式設計上也會給程式設計師帶來困擾...

基類指標指向派生類物件

派生類物件也 是 基類物件,但兩者不同。派生類物件可以當做基類物件,這是因為派生類包含基類的所有成員。但是基類物件無法被當做成派生類物件,因為派生類可能具有只有派生類才有的成員。所以,將派生類指標指向基類物件的時候要進行顯示的強制轉換,否則會使基類物件中的派生類成員成為未定義的。總結 基類指標和派生...