SV学习(5)——类和对象、类的成员
- 1. 类和对象概述
- 2. 声明类并创建对象
- 3. 赋值和拷贝,深拷贝浅拷贝
- 4. 对象的销毁
- 5. 句柄的使用
- 6. 静态变量
- 7. 静态方法
- 8. 验证为什么需要OOP面向对象
- 9. 一些差别对比
- 10. this
1. 类和对象概述
- class类:包含成员变量和方法,是软件盒子
- object对象:类在例化后的实例
- handle句柄(指针):指向对象的指针
- property属性(变量):在类中声明的存储数据的变量
- method方法:类中使用task或function定义方法,处理自身或外部传入的数据
- 原型声明(propotype):程序的声明部分,包含程序名、返回类型和参数列表
2. 声明类并创建对象
class Transaction;
bit [31: 0] addr, crc, data[8];
function void display;
$display ("Transaction: %h", addr);
endfunction: display
function void calc_crc;
crc = addr ^ data.xor;
endfunction: calc_crc
endclass: Transaction
initial begin
Transaction tr; // 声明句柄
tr = new(); // 创建对象
end
- 创建对象,开辟新的内存空间,用来存放新的成员变量和方法
- 创建对象时,会调用构建函数(可自定义),不需要返回值,隐式的返回例化后的对象指针
- 创建对象的动作时动态的,可以在仿真过程中发生
- SV的类在定义时,只需要定义构建函数(constructor),而不需要定义析构函数(destructor)
- 类定义时,需要定义构建函数,如果未定义,系统会自动帮助定义一个空的构建函数(没有形式参数,函数体为空)
3. 赋值和拷贝,深拷贝浅拷贝
-
声明变量和创建对象是两个个过程,也可以一步完成
Packet p1; p1 = new(): Packet p1 = new();
-
如果将t1赋值给另一个变量t2,实际也只有一个对象,只是指向这个对象的句柄有t1和t2,这种方式就是句柄的传递
Transaction t1, t2; t1 = new(); t2 = t1; t1 = new();
-
下面的方式表示p1和p2代表两个不同的对象。在创建p2的同时,将从p1拷贝其成员变量例如integer、string和句柄等,这种拷贝方式称为浅拷贝(shallow copy)
Packet p1, p2; p1 = new(): p2 = new p1;
-
浅拷贝VS深拷贝
// 假设有这一样一个class class packet_c; integer command; static int data = 1; packet_c pre; function new(int inival = -1); command = inival; endfunction endclass
- 浅拷贝即句柄拷贝,只拷贝对象中的数据变量,比如数组、句柄,但是它不复制对象,只复制了句柄,对于对象中的数据操作(一般为任务和函数)和其中定义的其他类的句柄,采用类似“引用”的方式,浅拷贝前后共用同一内存空间;
- 深拷贝即对象拷贝,对对象中的所有成员变量(包括数据变量、数据操作和其他句柄)统一分配新的内存空间;
4. 对象的销毁
- 没有句柄指向的对象就会被销毁,空间被收回
- SV不能回收一个被句柄引用的对象,即同一个对象被多个句柄指向,必须清除所有句柄才能销毁对象
5. 句柄的使用
-
句柄可以用来创建多个对象,也可以前后指向不同对象
Transaction t1, t2; t1 = new(); t2 = t1; t1 = new(); t2 = null; // 将t2赋值为‘空’,即不指向任何对象,此时指针‘悬空’,悬空的指针很‘危险’
-
可以通过句柄来使用对象中的成员变量或者成员方法
Transaction t; t = new(); t.addr = 32'h42; t.display();// 调用对象的成员方法
6. 静态变量
-
class中声明的变量默认为动态变量,声明周期始于对象创建,终于对象销毁
-
static声明class中变量为静态,声明周期始于编译阶段,贯穿于整个仿真阶段
-
静态变量var的引用:class::car、object.var
-
类的所有对象共享一个静态变量
class Transaction; static itn count = 0; int id; function new (); id = count++; endfunction endclass: Transaction initial begin Transaction t1, t2; t1 = new(); // id = 0, count = 1 t2 = new(); // id = 1, count = 2 $display("Second id = %d, count = %0d", t2.id, t2.count); // 同 Transaction::count end
7. 静态方法
- class中声明的方法默认是动态的,可以用static修改为静态方法
- 静态方法内可以声明并使用动态变量,但是不能使用类的动态成员变量
在调用静态方法时,可能并没有创建具体的对象,也因此没有为动态成员变量开辟空间,因此在静态方法中使用类的动态成员变量是禁止的,可能会造成内存泄漏,但是静态方法可以使用类的静态变量,因为静态方法和静态变量在编译阶段就已经分配好了空间。
class Transaction;
static Congif cfg;
static int count = 0;
int id;
// 通过静态方法来操作静态变量
static function void display_statics();
$display("Transaction cfg.mode = %s, count = %0d", cdf.mode.name(), count);
endfunction
endclass: Transaction
initial begin
Congif cfg;
Transaction::cfg = cfg; // 静态变量赋值
Transaction::display_statics(); // 调用静态方法
end
8. 验证为什么需要OOP面向对象
- 验证环境的不同组件其功能和所需要处理的数据内容是不相同的
- 不同环境的同一类型的组件其所具备的功能和数据内容是相似的
- 基于以上两点,验证世界的各个组件角色明确、功能分立,使用面向对象编程于验证世界的构建原则十分符合
9. 一些差别对比
9.1. Verilog的例化和SV class例化的差别:
- 两者共同点在于使用想用的模板来创建内存实例
- 不同点在于Verilog的例化是静态的,即在编译链接时玩车个,而SV class例化是动态的,可以在任意时间点发生,使得类的例化方式更加灵活和节省空间
- Verilog中没有句柄的概念,即只能通过层次化的索引方式A.B.sigX,而SV class通过句柄可以将对象的指针赋予其他句柄,使得操作更加灵活
9.2. 类与结构体的异同
- 二者本身都可以定义数据成员
- 类变量在声明之后,需要构造函数才会创建对象实体,而struct在变量声明时就开辟内存
- 类除了可以声明数据变量,还可以声明方法,而struct不行
- 根本上看,struct是一种数据结构,而class包含了数据成员以及针对这些成员们操作的方法
9.3. 类与模块的异同
- 从数据和方法定义而言,二者都可以作为封闭的容器来定义和存储
- 从例化来看,模块必须在仿真一开始就确定是否应该被例化,类的变量在仿真的任何时段都可以被构造(开辟内存)创建新对象。硬件部分必须在仿真一开始就确定下来,即module和其内部过程块、变量都应该是静态的;而软甲部分,即类的部分可以在仿真任何阶段声明并创建出新的对象
- 从封装性来看,模块内的变量和方法是对外部公共(public)开放的,而类可以根据需要确定外部访问的权限是否是默认的公共类型、或者受保护类型(protected)还是私有类型(local)
- 从继承性来看,模块没有任何的继承性,即无法在原有module基础上进行新module功能的扩展,而继承性是类的一大特点
10. this
- this是用来明确索引当前所在对象的成员(变量、参数、方法)
- this只可以用来在类的非静态成员、约束和覆盖组中使用
- this的使用可以明确所指变量的作用域
class Demo;
integer x;
function new (integer x);
this.x = x;
endfunction
endclass
标签:Transaction,变量,对象,句柄,成员,SV,new,class
From: https://www.cnblogs.com/amxiang/p/16754821.html