类和对象
一、类的基本概念
- 类class:是一种用来进行数据抽象的工具,将数据和对数据的操作封装在一起,
提供建立对象的模板
,可以看做是一种数据结构。 - 对象object:是所属类class的某一
特定实体
(也称实例)。 - 句柄handle:指向对象的指针,即
内存的基地址
。 - 属性property:类class的实体object中所包含的
各种变量
variable。 - 方法method:操作变量的
任务task和函数function
。
二、对象的创建和销毁
class word
byte nb[];
function new(int n);
nb = new[n]; //动态数组空间开辟
endfunction
endclass
initial begin : initial_1
word wd;//声明句柄
for(int i=1;i<=4;i++) wd = new(i); //创建了4个对象
end
initial begin : initial_2
#1ps
$display("How many Bytes are allocated for word instance?")
end
若wd = new(1)所需开辟的空间为1B,那么执行完开辟空间:
wd执行完,句柄指向第四个对象,4B。由于initial中是静态变量,即使initial执行完了,那么变量还在。
三、对象句柄的深拷贝 与 浅拷贝
1)、 浅拷贝:只拷贝对象中的数据变量,浅拷贝前后的数据变量使用不同的内存空间;而对于对象中的**数据操作(任务和函数)**和其中定义的其它类的句柄,采取类似“引用操作”的方式,浅拷贝前后共用同一内存空间。
BusTran bt0, bt1; //声明两个句柄
b1 = new(); //b1创建对象
b2 = new b1; //b2创建对象,同时对b1进行浅拷贝
2)、 深拷贝:对于对象中的所有成员统一分配新的内存空间,区别于浅拷贝。
BusTran bt0, bt1; //声明两个句柄
b1 = new(); //b1创建对象
b2 = new(); //b2创建对象
b2.copy(b1); //深拷贝,自定义copy函数
四、类的特性
class clock
local bit is_summer = 0;
local int nclock = 6;
function int get_clock();
if(!is_summer) return this.nclock;
else return this.nclock+1;
endfunction
function bit set_summer(bit s);
this.is_summer = s;
endfunction
endclass
clock ck;
initial begin
ck = new();
$display("now tome is %0d", ck.get_clock());
ck.set_summer(1);
$display("noe time is %0d", ck.nclock);
end
第一个打印为6;第二个编译出错。外部句柄ck不能访问nclock变量(加了限定local)。但可以访问没加限制的function。
4.1.封装
4.2.继承
子类继承了父类所有的成员方法和属性,并且可以拥有自己特性。通过关键字extends实现继承解决了代码的重用问题。
子类既包含继承父类的成员方法和属性,也有自己独特的个性方法(青出于蓝而胜于蓝),所以如果想要将一个指向父类的指针转化为指向子类的指针,无法直接转换,(父亲大人,时代变了!)必须通过$cast()
,在system veilog中被称为向下类型转换
(downcasting)。
- 向上类型转换:
transaction_class tr = new(); //父类
subtransaction_class sc = new(); //子类
tr = sc; //子类(右)指向父类(左),这是正确的可以直接转换
- $cast向下类型转换
transaction_class tr;//父类
subtransaction_class sc; //子类
sc = new();
tr = sc; //句柄类型转化为同一类型,父句柄类型转化为子句柄类型
$cast(sc,tr); // 通过cast方式可以实现,可以看到tr的句柄类型虽然是父类,但其指的对象类型是子类
子类继承父类时新增加一个函数,该函数与父类中的某函数同名,调用时会调用新增的函数,而不是继承下来的函数。
- 如果父类与子类的函数同名,但是参数不同,此时不论有无关键字virtual,父类的函数都将被隐藏。
- 如果父类与子类的函数同名,参数也相同,但是基类函数无关键字virtual,父类的函数都将被隐藏。(如果父类函数有virtual,则会形成多态)
对于子类类新增同名函数成员的访问:“对象名.成员名”;
对于父类中同名函数成员的访问:“(子类)对象名.基类名::成员名”
4.3.多态
调用同一个函数,实现不同的行为就是多态。但要满足两点要求:
- 必须通过父类的指针或者引用调用虚函数
-
被调用的函数必须是虚函数,且子类必须对基类的虚函数进行重写
。
虚函数virtual function
被virtual修饰的类成员函数称为虚函数。虚函数是动态绑定的,如果子类需要修改父类的行为(即重写与基类函数同名的函数),就应该在父类中将相应的函数声明为虚函数。使用虚函数应当注意:
- 父类中某一成员函数声明为virtual虚函数后,子类中的同名函数(同名、同参、同类型)自动生成虚函数。
- 子类的同名虚函数会
重写或覆盖
原来父类中的同名虚函数.。
虚类virtual class
虚类通过关键字virtual声明,不能被例化(不能创建对象),但可以通过派生,生成有用的子类,也可声明一个虚类的指针,通过抽象类的指针指向不同的子类对象。进而访问子类对象的虚函数,实现多态性。
正常情况下,父类是不可以访问子类中方法的,但是通过虚父类可以实现。
module OOP_EXTENDS();
class BusTran;
bit [31:0] addr,crc,data[7];
virtual function bit[31:0] calc_crc; //虚方法目的——实现多态
crc=addr^data.xor; //异或:奇数个1异或,结果为1;偶数个1异或,结果为0
calc_crc=crc; //返回值
endfunction
function void dsp_addr(input string handle);
$display("*****%s.ADDR = %0h",handle,addr);
endfunction
function void dsp_crc(input string handle);
$display("*****BusTran %s.crc = %h",handle,crc);
endfunction
endclass
class BadBusTran extends BusTran; //继承
bit bad_crc;
virtual function bit[31:0] calc_crc(); //虚方法目的——实现多态; 派生类中的同名函数的virtual可以省略
super.calc_crc(); //通过关键字super,实现子类对父类成员的调用
if(bad_crc) crc = ~crc;
calc_crc = crc;
endfunction
endclass
BusTrans bt0; //声明父类句柄
BadBusTran bbt0; //声明父类句柄
initial begin
bt0 = new;
$display("*****handle bt0 is : %0h",bt0);
bbt0 = new;
$display("*****handle bbt0 is : %0h",bbt0);
bt0.addr = 32'hFFFF_FFFF;
bt0.dsp_addr("bt0");
bbt0.addr = 32'h1111_1111;
bt0.dsp_addr("bbt0");
foreach(bt0.data[i]) begin
bt0.data[i] = 32'h0000_FFFF;
end
bt0.calc_crc;
bt0.dsp_crc("bt0");
foreach(bbt0.data[i]) begin
bbt0.data[i] = 32'hFFFF_FFFF;
end
bbt0.calc_crc; //此时未设置bad_crc,二值变量默认初始值为0
bbt0.dsp_crc("bbt0");
bbt0.bad_crc = 1;
$display("*****After bad crc set to 1");
bbt0.calc_crc;
bbt0.dsp_crc("bbt0");
end
//Polymorphism——多态
function bit[31:0] crc(BusTran bt); //多态——句柄传递过程中,会根据句柄自动识别所属类
crc = bt.calc_crc();
endfunction
bit [31:0] crc_value;
initial begin
#10;
crc_value = crc(bt0);
$display("*****crc from bt0 = %0h",crc_value);
crc_value = crc(bbt0);
$display("*****crc from bt0 = %0h",crc_value);
end
endmodule
包的使用
参考: