Verilog系列:virtual在类中的使用
作为一种面向对象的验证语言, SystemVerilog也秉承了面向对象"多态性(polymorphism)"的关键思想,使用virtual来声明虚方法和抽象类,通过虚方法和抽象类可以构造共享的基类或者方法.通过抽象类可以构造一个共享的基类,通过这个抽象类可以扩展出具有相似结构的子类.虚方法一般用于对基类中的方法进行重载,一般在基类中将可能在派生类中增加新特性的方法声明为virtual,这样派生类中可以重新定义该虚方法,即重载.下面将以具体示例展示virtual两种典型的用法.
【示例】
【仿真结果】
disp()在基类cobj和cobj0中定义为virtual,但是其派生类cobj1中重定义该方法时没有显式指明该方法为virtual,通过仿真可以观测到当句柄指向的类型为cobj0时调用的disp()来自于cobj0,指向的对象为cobj1时调用的是cobj1中重新定义的方法.
disp0()和disp1()在基类中均被声明为non-virtual,在75行p指向了p0(cobj0),虽然在cobj0中将disp0()和disp1()声明为virtual,但是实际上cobj0中的disp0()和disp1()并不能重载cobj中的disp0()和disp1(),即此时虽然p指向了p0,但是实际上调用的还是基类中的disp0()和disp1(),disp2()在cobj,cobj0,cobj1中均为non-virtual方法,所以调用的disp2()为句柄所对应的类型中定义的disp2().
82行p指向了p1(cobj1),因为基类p中disp()和disp1()均为non-virtual方法,所以此时调用的还是p的类型所对应的disp0()和disp1().
89行p0(cojb0)指向了p1(cobj1),disp0()和disp1()在cobj0中声明为virtual,相对于cobj0来说cobj1为其子类,所以在cobj0的弗雷中调用virtual声明的disp0()和disp1()对于cobj1来说是虚方法,所以p0在调用disp0()和disp1()时,调用的是cobj1中已经被重载重新定义的disp0()和disp1().
一般派生类在对基类方法进行重载时,要求两者的原型一致,但是存在一种特殊情况,当两者的返回类型兼容时,重载方法的类型可以与基类原方法不一样.
【示例】
【仿真结果】
cobj0继承于cobj,其中的get_pkt为虚方法,虽然方法在cobj和cobj0返回类型不同,但是两种类型是兼容的,所以在p通过p0.get_pkt()指向cobj0类型的返回值时,此时因为disp()为虚方法,p.disp()调用的并不是基类cobj中的disp(),而是派生类中的disp().
在SystemVerilog中,还存在一种较为特殊使用virtual的方法:纯虚方法(purevirtual method),这种方法主要用于抽象类(abstract class)中.抽象类可以被扩展但是不能直接被实例化,该类的定义以virtual关键字开始进行定义.纯虚方法是一种仅有方法原型但是没有方法实体,其中含有关键词组合pure virtual.纯虚方法只鞥在抽象类中进行定义,但是抽象类中不是只能定义纯虚方法.下面将通过具体示例展示抽象类和纯虚方法的使用.
【示例】
【仿真结果】
29行sp指向了p,抽象类不能直接实例化,但是可以通过其子类实例化后指向子类对象.
31行调用disp()时,因为cobj中并没有对应的方法,此时sp将依次从其指向对象的父类中逐级寻找disp()的定义.
32行调用disp0()时,因为该方法为虚方法,所以根据虚方法重载的特点,此时调用的是其指向对象的类中定义的disp0().
33行调用disp1()时,因为sp指向对象中并没有对应的方法定义,那么将从该对象的父类开始逐级寻找disp1()的定义,在sp中存在disp1()的定义.
ssp指向p之后调用各方法情况与上类似,不再赘述.
通过上述几个示例可以得出以下几点:
-
按照派生类存在的意义,派生类一般都会重载定义基类中定义的虚方法.
-
派生类可以重新定义虚方法,派生类中将要重载的方法定义中关键字virtual可有可无.
-
如果派生类没有重新定义某个虚方法,那么使用父类中定义的方法.
-
派生类中虚方法的声明必须与基类中的定义方式完全匹配,但是有一个例外,基类中定义的虚方法的返回类型如果与派生类中虚方法返回类型兼容,则也认为是类型匹配.
-
定义为virtual的方法是期望派生类重新定义的,基类希望派生类继承的方法建议不要定义为virtual方法.
-
抽象类中不仅可以有纯虚方法,还可以像其他类一样定义各种形式的方法,但是这里定义的纯虚方法仅有方法原型,并没有对应的方法体.
-
纯虚方法仅能定义于抽象类中.
-
抽象类可以被声明但是不能被实例化,但是可以指向已经实例化的子类.
-
抽象类的扩展类只有实现了抽象类中的所有纯虚方法后才能实例化.