一、封装
封装只是一个概念,指的是把对象的属性和行为包在一起的思想,好处是保证了类内部数据结构的完整性,类外面只能执行该类允许公开的数据。
二、继承
类的继承指子类继承父类的成员变量和成员方法,使用关键字extends指明,SV中一个子类不能有两个及以上的父类,一个父类可以有多个子类。示例:
typedef enum {RED,WHITE,BLACK} color_t;
class cat;
//protect color_t color;
color_t color;
string name;
//local bit is_good;
bit is_good;
function new();//若未自己定义new函数,则会自动调用super.new();函数
this.is_good = 0;
this.color = RED;
endfunction
function set_good(string ss,bit is_good);
this.is_good = is_good;
$display("%s is_good is %d",ss,this.is_good);
endfunction
endclass
class black_cat extends cat;
function new(string s1);
this.name = s1 ;
this.color = BLACK;
$display("cat name is %s",name);
endfunction
endclass
class white_cat extends cat;
function new(string name);
this.name = name ;
this.color = WHITE;
$display("cat name is %s",this.name);
endfunction
endclass
module test14;
black_cat bk;
white_cat wt;
string s1 = "black_cat";
string s2 = "white_cat";
initial begin
bk=black_cat::new(s1);
wt=new(s2);
bk.set_good(s1,1);
wt.set_good(s2,1);
$display("cat color is %s",bk.color);
$display("cat color is %s",wt.color);
end
endmodule
运行结果:
cat name is black_cat
cat name is white_cat
black_cat is_good is 1
white_cat is_good is 1
cat color is BLACK
cat color is WHITE
说明:
- 若类中没有写new函数(构造函数),那么系统会这个类自动添加一个默认的无参构造函数,是一个空函数体,只分配存储空间,不定义个性化的初值,每个没有继承关系的类都有且只有一个父类(object类),属于最高层次的类。
- 子类在定义new函数时,需要先调用父类的new函数或者super.new()。如果父类的new函数没有参数或者没有自定义new函数,子类也可以省略该调用,而系统会在编译时自动添加super.new()。
- 父类的new函数给父类变量方法开辟存储空间,子类的new函数给子类变量方法开辟存储空间,默认的new函数只负责开辟存储空间,不给个性化的初值,自定义new函数一般用于改变变量的初值,若自定义了new函数,则相当于重写了默认的new函数,也是多态的一种形式。
-
super.属性(行为) :访问父类中的属性(行为) ;this.属性(行为) :访问本类中的属性(行为) ,关于this指针可以参考(六)SV的类、类的成员操作。
三、多态
多态:对方法同一种调用方式产生不同的结果,即当一个类派生出子类的时候,基类中的一些方法可能需要被重写,同名的方法。多态常见形式:虚方法、类型转换
一、虚方法:
- 关键字:virtual
- 在派生类中,重写该方法的时候必须采用 一致的参数和返回值
- 虚方法可以重写其所有基类中的方法,而普通的方法被重写后只能在本身及其派生类中有效。
- 在多层继承关系的类中实现了同一方法的虚方法,该虚方法只在最后一个派生类中有效。
示例:
class packet;
int a =1;
int b =2;
function void display_a;
$display("packet::a is %0d",a);
endfunction
virtual function void display_b(bit [3:0] num1);
$display("num1 is %0d",num1);
$display("packet::b is %0d",b);
endfunction
endclass
class my_packet extends packet;
int a =3;
int b =4;
function void display_a;
$display("my_packet::a is %0d",a);
endfunction
virtual function void display_b(bit [3:0] num2);
$display("num2 is %5d",num2);
$display("my_packet::b is %0d",b);
endfunction
endclass
module top;
packet p1;
my_packet p2;
initial begin
p1=new();
p2=new();
p1 = p2;
p1.display_a;
p1.display_b(12);
end
endmodule
打印结果:
packet::a is 1
num2 is 12
my_packet::b is 4
说明:
- p1.display_a;display_a是普通方法,直接调用结束;p1.display_b;display_b是虚方法,调用 重写后的方法。
二、类型转换(也是多态的一种形式)
父类句柄指向子类,也就是子类转换为父类,是向上类型的转换
子类句柄指向父类,也就是父类转换为子类,是向下类型的转换
向上类型转换是安全的,向下类型转换是不安全的,因为父类本来在内存里就划好了一小块地盘,但是因为子类含有比父类更丰富的属性,它很有可能会访问父类并不包含的资源,所以会造成内存溢出。
向上类型转换:
示例:
class father;
string m_name;
function new (string name);
m_name = name;
endfunction
function void print ();
$display("Hello %s", m_name);
endfunction
endclass : father
class child0 extends father;
string car = "car";
function new (string name);
super.new(name);
endfunction
endclass : child0
class child1 extends father;
string plane = "plane";
function new (string name);
super.new(name);
endfunction
endclass : child1
module top;
father f;
child0 c0;
child1 c1;
initial begin
f = new("father");
f.print(); //调用打印函数
c0 = new("child0");
f = c0; //父类指针指向子类
f.print(); //调用同样的打印函数
c1 = new("child1");
f = c1; //父类指针指向子类
f.print(); //调用同样的打印函数
end
endmodule : top
打印结果:
Hello father
Hello child0
Hello child1
说明:
- 把父类指针指向子类,由于子类初始化变量的不同,所以printf函数,打印表现出不同的结果。
向下类型转换:
不能直接转换,可以通过动态类型转换函数$cast转换,但只有当前父类指针指向的对象和待转换对象的类型一致时,cast才会成功文章来源:https://uudwc.com/A/woB2y
class father;
string m_name;
function new (string name);
m_name = name;
endfunction
function void print ();
$display("Hello %s", m_name);
endfunction
endclass : father
class child0 extends father;
string car = "car";
function new (string name);
super.new(name);
endfunction
endclass : child0
class child1 extends father;
string plane = "plane";
function new (string name);
super.new(name);
endfunction
endclass : child1
module top;
father f;
child0 c0;
child1 c1;
child1 c2;
initial begin
f = new("father");
f.print();
c0 = new("child0");
f = c0;
f.print();
c1 = new("child1");
f = c1;
f.print();
c1.plane="big_plane";
$cast(c2,f);//向下类型转换
f.print();
$write(", has %s",c2.plane);
end
endmodule : top
说明:文章来源地址https://uudwc.com/A/woB2y
- cast用于扩展内存空间,避免内存溢出,因为c2与c1类型相同,f指向了c1,所以最终c2是指向了c1,c2没有new过,所以在类型转换之前没有分配内存空间