封裝可以隱藏實現細節,使代碼模塊化,繼承可以擴展已經存在的代碼模塊,目的都是為了代碼重用。多態是為了實現接口的重用。在SystemVerilog中,子類和父類之間多個子程序使用同一個名字的現象稱為SystemVerilog的“多態(polymorphism)”特征。子類從父類擴展創建之后,子類就繼承了父類的屬性和方法,這是SystemVerilog的繼承特征,但是這個繼承特征需要遵循一定的規則:
v 子類繼承父類的所有屬性(local除外)和方法;
v 子類可以添加新的屬性和方法;
v 子類可以重寫父類中的屬性和方法;
v 如果父類的一個方法被重寫,子類必須保持和父類的原定義有一致的參數;
v 子類可以通過super(不能使用super.super方式)操作符引用父類中的方法和成員;
v 被聲明為local的屬性或方法只能對自身可見,而對於外部和子類不可見;
v 被聲明為protected的屬性或方法,對外部不可見,對於自身和子類可見;
我們知道,因為繼承的特性,子類可以訪問父類的屬性,當子類對父類的方法進行重寫,子類句柄訪問的是重寫后的方法。那么當父類句柄指向子類時,是否可以通過父類句柄訪問子類的屬性呢?默認情況下,父類句柄是不能直接訪問子類的方法或者重寫父類的方法。
【示例】

【仿真結果】

注釋掉29行后,雖然pkt指向了spkt,但是pkt還是不能訪問其子類spkt中的vdisp方法,仿真會報如下錯誤。

示例中,通過父類句柄直接訪問子類的屬性是不能訪問到被子類的方法。那么通過父類句柄可以訪問被子類重寫的方法嗎?請看下例。
【示例】

【仿真結果】

可見,雖然子類對父類中的方法進行了重寫,並且將父類句柄指向了子類對象,但是父類句柄和子類句柄還是訪問各自類型中的方法。那么如何可以實現通過父類句柄對子類重寫屬性的訪問呢?
【示例】


【仿真結果】

示例中,pkt具有如下圖的屬性和方法,其中vdisp為虛方法,call_vdisp為pkt的方法,其中調用了vdisp,disp和call_disp均為非虛方法。在pkt沒有指向spkt時,pkt訪問disp、call_disp、vdisp、call_vdisp四種方法都是packet中的四種方法。

spkt具有的屬性如下圖:

在SystemVerilog中,子類對象的空間分配分為兩個部分(如上圖所示),一部分來自父類的繼承部分,一部分是重寫或新增的部分。spkt訪問disp是,因為disp為其父類的非virtual方法,雖然sub_packet對其進行了重寫,但是當spkt對其訪問時,訪問的是sub_packet定義的disp。vdisp訪問情況同disp。當spkt訪問call_disp時,因為sub_packet中沒有定義call_disp,那么程序會訪問內存中父類中的call_disp,call_disp是一個普通的方法,此時call_disp中調用的disp也是一個普通方法,所以此時的調用均為spkt對象中的pkt部分。當spkt仿call_vdisp時,因為call_vdisp是一個普通方法,所以此時會調用spkt中的pkt部分,但是在call_vdisp中調用的vdisp為pkt中的虛方法,並且該虛方法在spkt中進行了重定義,所以此時call_vdisp調用的vdisp為spkt中重寫的vdisp。所以在spkt調用四個函數時,只有對call_disp的調用顯示的是pkt中輸出的信息。
當pkt指向spkt之后,pkt調用disp、call_disp、vdisp和call_vdisp這四種方法的實際調用訪問順序如下圖所示:

當pkt調用這幾種方法時,程序同樣會找到spkt中的pkt部分,此時如果發現調用的方法是虛方法,那么就會在spkt中查找重寫的對應的方法,如果確實存在重寫的方法,那么此時就會調用spkt中的重寫的方法。如果沒有則會執行pkt部分的方法。
spkt調用disp、call_disp、vdisp和call_vdisp這四種方法的實際調用訪問順序如下圖所示:

當spkt調用這幾種方法時,對於spkt中的存在的方法將會直接調用,對於spkt不存在的則會訪問pkt部分,在訪問pkt中的方法時,如果其調用的方法為pkt中定義的虛方法,且該方法在spkt中重寫,那么此時pkt中的該方法將調用spkt中重寫過得方法,如示例中對call_vdisp的調用。
通過上述幾個示例,可以看到聲明一種方法為虛方法的方式就是在原來方法前添加關鍵字virtual,子類中重寫的方法可以不用添加virtual關鍵字,並且子類中重寫的方法要和父類中該方法的參數和返回值一致。當將父類中將要被重寫的方法在聲明時指定為virtual方法,那么當父句柄指向子類對象的空間,此時可以實現父句柄對子類重寫父類方法的訪問。在SystemVerilog中子類對象的空間分配分為兩個部分,一部分來自父類的繼承部分,一部分是重寫或新增的部分。默認情況下,父類的方法是無法訪問派生類的重寫和新增部分的,如果希望重寫的方法被父類看到,就需要依靠本示例中的virtual方法,而虛方法正是OOP中基本的多態性結構。那么,哪些方法要定義為virtual方法呢?經常需要對從父類繼承的方法進行一定的修改以適應其應用的需要的方法,在父類中對該方法定義的時候需要添加virtual關鍵字將該方法聲明為虛方法。
綜上所述,關於虛方法我們得到以下注意事項:
-
當使用了virtual方法,那么SystemVerilog會根據對象的類型決定調用哪個虛方法,而非句柄的類型;
-
當沒有使用virtual方法,那么SystemVerilog會根據句柄的類型決定調用哪個虛方法,而非對象的類型;
-
如果父類的一個方法被重寫,子類必須保持和父類的原定義有一致的參數;
虛方法與重寫的實現就是多態!當父類的對象指向不同的子類的時候,虛方法就表現出了不同的實現方法,呈現多態!
更多技術內容,可關注下圖個人技術微信公眾號,歡迎朋友們關注溝通!

本文純屬學習之用,歡迎指正文中不足,
封面圖片若有侵權,請及時溝通!
