反彙編探究:為什麼子類引用不能指向父類對象

在java、C++等面向對象的語言中,實現多態的方式就是使用父類引用指向子類對象,所以父類引用指向子類對象是沒有任何問題的,但是,大家有沒有想過,子類引用可以指向父類對象嗎?答案是不可以!但是為什麼呢?


下圖是在java中,使用子類引用指向父類對象的情況

反彙編探究:為什麼子類引用不能指向父類對象

編譯可以通過,因為對生成的Person對象做了一個強制轉換,騙過了編譯器,其本質上還是屬於子類引用指向父類對象。

點擊運行,出現下圖的報錯情況。

反彙編探究:為什麼子類引用不能指向父類對象

很明顯,java虛擬機在運行該行代碼的時候進行了運行時檢測,禁止子類引用指向父類對象。

所以,這種操作在java裡面是不允許的,接下來,我們把代碼拷貝一下,在C++的環境再跑一下。

反彙編探究:為什麼子類引用不能指向父類對象

編譯,運行,一切非常順利。

為什麼java裡面不允許這種操作,而C++卻允許這種操作呢?我們接下來在C++的環境下,反彙編窺探一下這寫代碼究竟幹了些什麼事。


首先,在執行這行代碼的時候,先把一個4壓入棧中,然後去調用operator new這個函數,很明顯,這個4就是該函數的一個參數,它完成的任務就是,向堆空間申請4個字節的存儲空間,為什麼是4個字節?因為Person這個類裡面只有age這一個屬性,因此new出來的對象也只需要4個字節存儲就夠了。然後使用stu這個Student類型的指針指向這4個存儲空間的首地址。

反彙編探究:為什麼子類引用不能指向父類對象

反彙編探究:為什麼子類引用不能指向父類對象


接下來,我們來看一下,下面兩行的反彙編代碼,因為Student類繼承於Person類,因此Student類裡面有age和stuId兩個變量,又因為是公有的,所以stu可以訪問這兩個變量的地址,我們對這兩個值進行賦值操作。

這兩個賦值操作的反彙編代碼如下,可以明顯看出,它們都是先找到stu指向的Person對象的堆空間首地址,然後當給age賦值為18時,是把12h(18的十六進制)塞給Person對象首地址位置開始的4個字節,當給stuId賦值為2時,是把2(2的十六進制)塞給Person對象首地址+4位置處開始的4個字節,

反彙編探究:為什麼子類引用不能指向父類對象

反彙編探究:為什麼子類引用不能指向父類對象

反彙編探究:為什麼子類引用不能指向父類對象


大家可以看到上圖,很明顯,相信大家就看出問題來了。。

因為new Person()只申請了4個字節的存儲空間,而你現在卻越界使用了沒申請到的後面4個字節存儲空間,然後把2賦值給了這4個字節的存儲空間中。


這會導致什麼問題?因為後面的這4個字節沒有被你申請到,那麼該4個字節可能是其他的一些數據,那麼你的這個行為會覆蓋掉別的數據,或者這4個字節還是空閒的,以後可能被其他的數據覆蓋,所以這是一種不安全的行為。


因此,無論在C++還是java中,都是應該禁止掉這種行為的,只是java做了運行時檢測,而C++並沒有而已。


分享到:


相關文章: