問題一:動態類型轉換和靜態類型轉換的區別?
$cast
:基本語法$case(A,B)
實際上是A=B;A表示目的端,B表示源端。(downcasting)類型向下轉換
- $cast 動態類型轉換,轉換失敗會報錯。
- `靜態類型轉換,轉換時報不報錯
問題二:$cast是function還是task?
據語境,仿真器會自動選擇執行task或是function,task在不需要返回值時執行,而function在需要返回值的語境下執行。將cast作為任務還是函數調用確定了無效賦值是如何處理的。
問題三:什么時候需要$cast?
我們通過下面的一個簡單的例子來進行說明一下。
【多選題】有如下的代碼,下面$cast返回值為1的有:(BCD)
class A; endclass;
class B extends A; endclass;
class C extends B; endclass;
A a = new(…);
B b = new(…);
C c = new(…);
A、$cast(b, a)
B、a = c; $cast(b,a)
;
C、$cast(b,c)
D、a = b; $cast(b, a)
解析:,
- 在解析之前,希望大家能夠通讀一下綠皮書的第八章8.3.1,借用書上的一句話,將一個基類句柄賦值給一個擴展類並不總是非法的,當基類的句柄確實指向一個派生類對象時是允許的。
$cast
子程序會檢查句柄所指向的對象類型,而不僅僅檢查句柄本身。比如說$cast(B,A)
,會檢查句柄A指向的對象是不是句柄B指向的對象的擴展類,或者是同一類型。如果滿足的話,你就可以從基類的句柄A中拷貝擴展對象的地址給擴展對象的句柄B了。
看到這里大家應該會有點暈暈的,那我們通過實際的例子來給大家展示一下,cast的實際應用場景。
class bird;
virtual function void hungry();
$display("I am bird,I am hungry");
endfuntion
function void hungry2();
$display("I am bird,I am hungry2");
endfuntion
endclass
class parrot extends bird;
virtual function void hungry();
$display("I am parrot,I am hungry");
endfuntion
function void hungry2();
$display("I am parrot,I am hungry2");
endfuntion
endclass
program ex;
bird A;
parrot B;
initial begin
A = new();
B = new();
A.hungry();
A.hungry2();
B.hungry();
B.hungry2();
end
endprogram
運行結果:
I am bird,I am hungry
I am bird,I am hungry2
I am parrot,I am hungry
I am parrot,I am hungry2
相信大家對這個結果夠沒有異議,那么接下來我將通過幾種場景來深入理解cast。我們假設bird parrot的類不變,改寫program的調用。
情景一:基類 = 擴展類
program ex;
bird A;
parrot B;
initial begin
A = new();
B = new();
A = B;
A.hungry();
A.hungry2();
B.hungry();
B.hungry2();
end
endprogram
運行結果:
I am parrot,I am hungry
I am bird,I am hungry2
I am parrot,I am hungry
I am parrot,I am hungry2
- 基類 = 擴展類;A句柄指向parrot的對象
- virtual函數的特性
- A=B,A指向B,基類指向擴展類,在擴展類中都能找到基類的東西,所以是OK的。
- B=A,B指向A,擴展類指向基類,擴展類的一部分東西在基類里面是找不到的。所以會報錯。
場景二:擴展類 = 基類
program ex;
bird A;
parrot B;
initial begin
A = new();
B = new();
B = A;
A.hungry();
A.hungry2();
B.hungry();
B.hungry2();
end
endprogram
運行結果:
Error-[SV-ICA] illegal class assignment
- 擴展類 = 基類,直接調用會有問題
- 參考場景一
場景三:cast用作task
program ex;
bird A;
parrot B;
initial begin
A = new();
B = new();
$cast(B,A);
A.hungry();
A.hungry2();
B.hungry();
B.hungry2();
end
endprogram
運行結果:
Error-[DCF] Dynamic cast failed
$cast(擴展類,基類)
,上述代碼$cast
沒有返回值,所以結果是做task用,根據圖【cast的task function應用】我們可以知道,如果cast用作task應用的時候失敗,則異常結束,直接跳出。
場景四:cast用作function
program ex;
bird A;
parrot B;
initial begin
A = new();
B = new();
if(!$cast(B,A))begin
$display("B = A; failed");
end
A.hungry();
A.hungry2();
B.hungry();
B.hungry2();
end
endprogram
運行結果:
B = A;failed
I am bird,I am hungry
I am bird,I am hungry2
I am parrot,I am hungry
I am parrot,I am hungry2
if(!$cast(擴展類,基類))
,上述代碼$cast
有返回值,所以結果是做function用,根據圖【cast的task function應用】我們可以知道,如果cast用作function應用的時候失敗,返回0,繼續運行。
場景五:cast成功轉換的應用,B=A,基類的句柄A指向擴展類,擴展類跟B句柄指向的類是同類型的。
program ex;
bird A;
parrot B;
initial begin
A = new();
B = new();
A = B;
if(!$cast(B,A))begin
$display("B = A; failed");
end
else begin
$display("B = A; OK !!!!");
end
A.hungry();
A.hungry2();
B.hungry();
B.hungry2();
end
endprogram
運行結果:
B = A;OK !!!!
I am parrot,I am hungry
I am bird,I am hungry2
I am parrot,I am hungry
I am parrot,I am hungry2
-
B=A,基類的句柄A指向擴展類,擴展類跟B句柄指向的類是同類型的。(參考書上的那句話,這里通過例子深入理解一下)
-
A的基類句柄指向的是擴展類的對象
-
B是擴展類的句柄,指向的是擴展類的對象
-
B=A,所以B可以指向A,但是要用$cast做類型轉換。
場景六:cast成功轉換的應用,B=A,基類的句柄A指向擴展類,擴展類是B句柄指向的類的擴展類。
class dog extends parrot;
virtual function void hungry();
$display("I am dog,I am hungry");
endfuntion
function void hungry2();
$display("I am dog,I am hungry2");
endfuntion
endclass
program ex;
bird A;
parrot B;
dog C;
initial begin
A = new();
B = new();
C = new();
A = C;
if(!$cast(B,A))begin
$display("B = A; failed");
end
else begin
$display("B = A; OK !!!!");
end
A.hungry();
A.hungry2();
B.hungry();
B.hungry2();
end
endprogram
運行結果:
B = A;OK !!!!
I am dog,I am hungry
I am bird,I am hungry2
I am dog,I am hungry
I am parrot,I am hungry2
- B=A,基類的句柄A指向擴展類,擴展類是B句柄指向的類的擴展類。(參考書上的那句話,這里通過例子深入理解一下)
相信通過上述的六種場景,大家對cast應該有了一定的了解,那么對我們最開始的那道題,大家應該都能理解了吧。
總之是一句話:在指向的時候,一定要在目的地的對象能找到我的存在。
- 基類指向擴展類的時候,總是OK的,因為基類在擴展類中總是可以找到對應的存在。
- 擴展類指向基類的時候,要用$cast做類型轉換。這個時候要看基類的句柄指向的是對象是不是包含擴展類,如果是轉換就會成功。