赋值操作,如下图所示代码块:
1. program test;
2. class base_cls;
3. int a=100;
4. endclass
5.
6. initial begin
7. base_cls cls_0;
8. base_cls cls_1;
9. cls_1 = new();
10. cls_0 = cls_1;
11. end
12. endprogram
实际上和使用cast类似,直接翻译为,将一个类的句柄赋值给另一个类,深入理解的含义为:类的赋值实际上是将被赋值的类句柄指向赋值类句柄所指向的内存空间,本质上与1.2节的图一样,也就是:(当被赋值的类不进行实例化时)
如果当被赋值的类实例化时:
因此,改变一个值,另一个值也会跟着改变。
再看一个复杂一点的例子,在base_cls中例化了一个其他类,并在base_cls的new函数中实例化这个类。
1. program test;
2.
3. class inner_cls;
4. int in_a = 200;
5. endclass
6.
7. class base_cls;
8. int a=100;
9. inner_cls i_a;
10. function new();
11. i_a = new();
12. endfunction
13. endclass
14.
15. initial begin
16. base_cls cls_0;
17. base_cls cls_1;
18. cls_1 = new();
19. cls_0 = cls_1;
20. end
21. endprogram
此时类的赋值操作,可以按如下图所示表示:
同样的,如果采用cls_0如果也采用new函数,如下图:
此时无论改变base_cls的成员变量a,还是改变base_cls内部inner_cls的成员变量in_a,cls_0实例和cls_1实例都会跟着变。
cls_0不适用new函数,结果一样,因为指向了同一块存储空间
SV浅拷贝是通过new函数实现的,只不过在调用new函数的时候,后面跟一个要复制的类,基本语法如下:
1. program test;
2. class base_cls;
3. int a = 100;
4. endclass
5.
6. initial begin
7. base_cls a;
8. base_cls b;
9. a=new();
10. b=new a;
11. end
12. endprogram
浅拷贝的含义,上述代码块中其实没有很好的体现,浅拷贝的核心点是:拷贝的对象只会拷贝最高层次的类,也就是说会新开辟一块内存,复制最顶层的类,而深层次的类不会进行拷贝。如下图所示代码块:
1. program test;
2.
3. class inner_cls;
4. int in_a = 200;
5. endclass
6.
7. class base_cls;
8. int a = 100;
9. inner_cls i_a;
10. function new();
11. i_a = new();
12. endfunction
13. endclass
14.
15. initial begin
16. base_cls cls_0;
17. base_cls cls_1;
18. cls_1=new();
19. cls_0=new cls_1;
20. end
21. endprogram
这个过程,可以通过下图表示:
如上图可以看到,浅拷贝过程中,inner_cls类,被拷贝的类内部,其实是和cls_1内部的inner_cls共享内存,仿一下看看:
如果改变深层次类的属性呢?
会跟着改变,因为复制类和被复制类之间,深层次类是会共享内存空间的。
深拷贝需要我们自己在类中写拷贝函数,可以返回这个类本身,也可以不返回,通过类的调用传参进行复制。如下图所示代码块:
1. program test;
2.
3. class inner_cls;
4. int in_a = 200;
5. function void copy(inner_cls c);
6. c.in_a = this.in_a;
7. endfunction
8. endclass
9.
10. class base_cls;
11. int a = 100;
12. inner_cls i_a;
13. function new();
14. i_a = new();
15. endfunction
16. virtual function void copy(base_cls bc);
17. bc.a = this.a;
18. bc.i_a.copy(this.i_a);
19. endfunction
20. endclass
21.
22. initial begin
23. base_cls cls_0;
24. base_cls cls_1;
25. cls_0=new();
26. cls_1=new();
27. cls_0.copy(cls_1);
28. end
29. endprogram
这个时候,通过类内部定义的拷贝函数,实现类的深拷贝,此时相当于开辟了两块存储空间,实现类的深拷贝,图示为:
心细的读者可以看到,上面的代码第26行,事先将被拷贝的类的实例要new出来,这是为什么呢?读者可以发现,在调用copy函数时,copy函数并不会帮你new出来一个新的类,因此,如果不事先实例化,会报错。仿一下看看:
删除new试试:
直接报错!
那如果我们在拷贝函数中new了一个类,并把这个类返回,是否可以不用new?例如如下代码块:
1. program test;
2.
3. class inner_cls;
4. int in_a = 200;
5. function inner_cls copy();
6. inner_cls c;
7. c = new();
8. c.in_a = this.in_a;
9. return c;
10. endfunction
11. endclass
12.
13. class base_cls;
14. int a = 100;
15. inner_cls i_a;
16. function new();
17. i_a = new();
18. endfunction
19. virtual function base_cls copy();
20. base_cls bc;
21. bc = new();
22. bc.a = this.a;
23. bc.i_a = this.i_a.copy();
24. return bc;
25. endfunction
26. endclass
27.
28. initial begin
29. base_cls cls_0;
30. base_cls cls_1;
31. cls_0=new();
32. cls_1 = cls_0.copy();
33. end
34. endprogram
可以发现在拷贝函数内部,新定义了一个类,然后将这个类new出来,复制之后返回,此时外部新定义的类就无需执行new函数,因此在copy函数体内,已经通过new函数分配好了内存空间。仿一下试试:
试着实例化一下呢?其实是多此一举,但是实例化也不妨碍仿真结果。读者可以自行试试。
实际上,这种方式在UVM里有一处用到了,就是在uvm_object基类中定义的clone()函数,实现对于继承自uvm_object的类,比如uvm_sequence,uvm_transaction等非组件类型的类的复制,此时被复制的类无需实例化,因为在clone函数里,会将被复制的类实例化好,并把指针返回。
值得注意的是,clone函数并不能复制component组件,为什么呢?因为component在例化时,需要在create函数中,写明当前component是实例化在哪个parent节点中的,以维持一个树形结构。clone函数并不会指明这一点,因此如果调用组件的clone,就会造成被复制出来的组件不知道在哪个parent节点下,出现一些异常。