$cast在子类与父类之间的复制
1 子类cast给父类:$cast(father_cls, child_cls)
之前有提到,$cast是将两个类型强制转换,$cast(A,B),将B强制类型转换给A,应用在类上,就是句柄的强制赋值操作,那么在子类和父类之间的赋值呢?其实是原理是一样的,规则也只能是子类的句柄赋值给父类,如下图所示代码块:
1. program cls_cp;
2. class base_cls;
3. int a = 100;
4. inner_cls i_a;
5. function new();
6. i_a = new();
7. endfunction
8. endclass
9.
10. class child_cls extends base_cls;
11. int b = 300;
12. endclass
13.
14. initial begin
15. base_cls base_0;
16. child_cls child_0;
17. child_0 = new();
18. $cast(base_0, child_0);
19. end
20.
21. endprogram
且经过cast后,等同于赋值操作,即父类的句柄指向子类的对象,且只能访问子类对象的继承类的成员变量。仿一下看看:
反过来cast,直接报错:
2. 父类cast给子类:$cast(child_cls, father_cls)
上文提到,父类是不能cast给子类的,那么在什么情况下可以呢?其实很容易想到,就是在父类的句柄指向子类的对象的时候,cast可以生效,如下图所示代码块:
1. program cls_cp;
2. class base_cls;
3. int a = 100;
4. inner_cls i_a;
5. function new();
6. i_a = new();
7. endfunction
8. endclass
9.
10. class child_cls extends base_cls;
11. int b = 300;
12. endclass
13.
14. initial begin
15. base_cls base_0;
16. child_cls child_0;
17. child_0 = new();
18. base_0 = child_0;
19. $cast(child_0, base_0);
20. end
21.
22. endprogram
这样的化,通过父类的cast,其实就是子类的对象,同样的父类也不能访问子类本身的成员变量,父类改变自身的成员变量,子类调用自己父类的成员变量时也会发生改变。仿一下看看:
修改父类的成员变量,子类也会跟着改:
实际上,这种赋值是没有意义的,类的赋值要体现在不同的对象之间,用这种方式可以实现父类作为桥梁赋值给另一个子类的对象,在UVM中得到了大量的使用。
改一个,另一个也跟着改变,读者可以自行尝试改变child_1的成员变量,以及改变cls_0的成员变量,看是否另外的两个类成员变量也发生改变。值得注意的是,cls_0依然无法访问child_0和child_1中的成员变量。过程如下图所示:
3. 两个不同的子类之间的cast
同一个子类的不同对象之间是可以cast的,那么不同子类直接能否cast?能否通过父类进行cast?这里先给出结论:都是不可以的,原因是cast在对于类进行操作时,认为相同类型的两个句柄,可以cast,那么如果两个不同子类声明的句柄,cast会认为是不同的类型,因此不能转换,即使他们继承自一个父类,同样通过父类的cast也是不可以的。如下图仿真:
直接报错,通过父类进行不同子类之间的cast,也不可以:
$cast在子类与父类之间深拷贝的用法
在3.2节理解了cast在父类和子类的用法后,本节以一个具体的实例,说明cast在深拷贝中的应用,例如如下代码块:
1. program cls_cp;
2.
3. class inner_cls;
4. int in_a = 200;
5. function void copy(inner_cls ii);
6. ii.in_a = this.in_a;
7. endfunction
8. endclass
9. class base_cls;
10. int a = 100;
11. inner_cls i_a;
12. function new();
13. i_a = new();
14. endfunction
15. virtual function void copy(base_cls i_c);
16. i_c.a = this.a;
17. i_c.i_a.copy(this.i_a);
18. endfunction
19. virtual function void display();
20. $display("a in father cls is %d ", a);
21. endfunction
22. endclass
23.
24. class child_cls extends base_cls;
25. int b = 300;
26. function void display();
27. $display("b in child cls is %d ", b);
28. endfunction
29.
30. function void copy(base_cls c);
31. child_cls cc;
32. super.copy(c);
33. $cast(cc, c);
34. cc.b = this.b;
35. endfunction
36.
37. endclass
38.
39. initial begin
40. child_cls child_0;
41. child_cls child_1;
42. child_0 = new();
43. child_1 = new();
44. child_1.copy(child_0);
45. end
46.
47. endprogram
分析:在子类中重载了父类的copy函数,重载的过程是,先声明了一个子类,然后调用父类的copy函数,将父类的成员变量进行深拷贝赋值,包括父类的深层次成员变量inner_cls,然后再通过$cast方法,将父类赋值给子类,这样在子类cc中,就有了父类成员变量的复制本,最后将子类的成员变量进行复制,完成深拷贝。
此处其实有一个问题,就是之前不是说父类不能够cast给子类吗?为什么这里可以写,因为在实际调用的时候,是两个子类之间的复制,通过调用子类的copy函数,copy函数的参数传递的是子类的句柄,在函数传参的过程中,也就将形式参数父类的句柄指向了实参子类的对象,因此可以cast。
仿真结果:
读者可以自行尝试注释行的仿真,结果应该是一样的。
UVM里用到了大量这样的方法,掌握这个原理,对于理解UVM的应用,以及SystemVerilog更高级的建模,都十分有帮助。