第七章:继承与多态

验证过程中经常会有新的测试用例(DUT)被加进来,原有的验证平台就需要做对应的修改,可能会引入意外错误。为此,SV提供类的继承和多态操作,使得被调用的方法能够动态的选择实现方式。

类的继承

类的拓展与类成员的重写

一个类可以被拓展,产生一个派生类。派生类(子类)也就继承了基类(父类)所有的属性。一些修改就可以在子类中完成。也就是类的重写

子类可以通过super操作符来引用父类中的方法和成员,以区分重写的属性或方法。

使用extends关键字来扩展类:

class PACKET;  //基类(父类)
  integer status=10;  //父类的数据成员
  task showstatus(); //父类的方法_任务
    $display(status);
  endtask
  function int chkstat(int s);  //父类的方法_函数
    returen(status==s);
    endfunction
endclass

class drivedpacket extends PACKET; //类的拓展:drivedpacket为PACKET的子类
  integer status=15;  // 重写:改写数据成员status的值
  integer b,c,d; // 增加了新的数据成员b,c,d
  function int chkstat(int s); // 重写:修改了函数chkstat的功能
    chkstat=super.status+status; //使用super操作符引用父类成员
    $display(status);
  endfunction
endclass

以上代码使得存储空间多出一个子类drivedpacked对象,其分配空间不仅有来自父类继承而来的数据和方法,还有自己添加以及修改的部分。

 

子类对象对父类对象的赋值

1.子类对象赋值给父类对象是合法的,而父类对象赋值给子类对象不一定合法,2.可以通过$cast来检查赋值是否合法。

3.通过父类对象去引用子类中已经重写的属性或者方法,结果只会调用父类。

4.通过子类对象可以直接访问重写属性或方法

module drivered_base;
  initial begin
    drivedpacket p1=new;  //构造子类对象,其父类自动构造(③)
    packet p2=p1;   //将子类句柄赋值给父类,使父类指向子类(1.)
    j=p1.status     //j的结果为15,也就是访问了重写属性(4.)
    j=p2.status  //j的结果为10,也就是尽管指向了子类,但依然访问的是子类中的父类属性(3.)
    $cast(p1,p2); //将父类p2赋给子类p1,检查是否合法(2.)
  end
endmodule

构造函数的调用

在new函数中定义的任何代码执行之前,new()执行的第一个默认动作是调用父类的new(),并且会沿着这种继承关系一直向上回溯调用。

 

如果父类的构造函数需要参数,则有两种办法传递:第二种是最常用的办法

  1. 在类的扩展时指定:class drivedpacket extends packed(5);
  2. 在构造函数中使用super来传递父类参数:
  3.   function new();
         super.new(5);
      endfunction

虚方法与多态

在类的扩展中,新增属性和方法对父类不可见,因此以下代码:

子类仅重写了build_payld方法,在子类对象der中调用了内嵌了build_payld方法的build_packed方法时,由于子类重写方法对父类不可见,所以该对象调用的依然是子类空间中继承的父类方法,也就是未被重写的方法,调用过程如下图:

 

因此,为了使重写的方法可以被父类看到,sv提供了虚方法

虚方法

普通的方法在重写后仅对本身以及其子类中有效,而不能够被父类看到

虚方法的定义:使用virtual关键字

 

一开始p1和p2空间独立:

 

当父类对象p1指向子类对象p2后,p1.printA被调用时会访问空间中p2中的p1部分,发现PrintA为普通方法时,执行调用后结束。过程如下:

 

调用p1.printB时,程序同样找到空间中p2中的p1部分,而此时找到的printB声明成了虚方法,这时会咨询系统,查看该方法是否被p2对象或其子类对象所重写,一直查询到最后被重写的那个方法去调用(动态查找)。过程如下:

 

注意:一旦类中的一个方法被声明成了虚方法,那么在后续的继承过程中,无论是否使用关键字,他就一直是一个虚方法。因此,每一个类的继承关系,只有一个虚方法的实现,而且是在最后一个派生类中

多态

虚方法是类中声明的非静态方法,其带有关键字virtual,称带有虚方法的类为多态类。

继承是为了实现代码重用,而多态则是为了接口重用。多态提供了相同的操作接口,但能适用于不同的应用要求,当基于父类所设计的程序需要适应子类操作变化时,就可以采用虚方法来通知编译器这种可能的变化。

注意:只有当父类对象指向不同的子类对象时,虚方法可以表现出不同的实现方法,且都可以通过父类对象实现访问。如果父类对象没有指向子类对象,即便父类中声明了虚方法,父类对象调用该虚方法时也不会进行动态查找,而是直接调用父类方法(见①中代码p1=p2之前的结果)

子类句柄赋值给父类除了直接赋值(p1=p2),还可以通过函数调用的方式赋值:

process是某一个模块中的任务,形参tr是一个父类对象,在process(etr)语句中将实参etr传递给形参tr,也就是子类句柄传递给了父类句柄,也就是相当于执行了语句:tr=etr;由于类中compute_crc方法不是虚方法,所以执行process(etr)任务时,tr.compute_crc()依然调用的是父类方法。

虚类和参数化类

为了进一步实现代码重用,sv还提供了虚类(抽象类)和参数化类(模板)

虚类

我们可以从一个基类派生出很多的子类,也就是说,一个基类定义了很多子类的原型,于是我们可以抽象出一个类来说明子类的共有特征,这个抽象的类就是虚类,同样的使用virtual来定义。

virtual class base_packet;

虚类一般不用来定义对象,通常其内部会定义虚方法,而该虚方法只提供原型,而没有具体实现,也就是不需要写函数体,因此被称为纯虚方法,使用pure关键字来定义:

pure virtual function int send(bit [31:0] data);

注意:只有虚类可以定义纯虚方法,简而言之,虚类就是一个抽象的类模板。不可以实例化。

虚类同样可以扩展为非虚类,且扩展为非虚类后,其内部的纯虚方法必须被重写,也就是要提供具体实现:

class ether_packet extends basepacket;

  virtual int function send(bit[31:0] data);

  具体实现

 endfunction

endclass

参数化类

参数化类常用来修改例化对象的数组大小和数据类型:

1.参数化对象数组大小

class vector #(int size=1); //此时vector是一个参数化的类,将数组长度参数化,
  bit[size-1:0] a;          参数size的初始值为1
endclass
  
vector #(10) V1;        //对象V1的数组长度为10
vector #(.size(2)) V2;    //对象V2的数组长度为2
typedef vector #(4) V3  //V3是size=4的vector类(为避免重复声明参数类,常用typedef)

 2.参数化对象数据类型

class stack #(type T=int);  //参数化类型T,参数T的初始类型为int
  T item[];      //定义了T类型的动态数组
endclass

stack S1;  //对象S1的数组类型为缺省值:int
stack #(bit[1:10]) S2;  //对象S2的数组类型为10位向量(bit [1:10]item[])
typedef stack#(V3) S3;  //S3是一个类

 3.参数化类的扩展extends

class c#(type T=bit);...endclass   //c为基类
class d #(type P=real) ectends c#(intenger); //d类继承了参数为integer的c类

 约束重写

B类中对约束进行了修改,约束与虚方法类似,将子类句柄赋值给父类句柄后,调用随机函数,会动态查找重写约束。而无需用virtual声明。

 

数据的隐藏与封装

在sv中,未被限定的类属性和方法默认是公共的(public)

被声明为local的数据成员或方法:只对自身可见,对于外部或者子类不可见

被声明为pritected的数据成员或方法,对外部不可见,对自身和子类可见。

在指定这些修饰符的时候没有预定义的顺序; 然而, 对于每一个成员它们
只能出现一次, 也就是不可以将成员同时定义为 local 和 protected。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值