SystemVerilog 面向对象:类、句柄、继承、多态与虚方法
更新时间:2026-10-01。UVM 的一切——driver、monitor、sequence——都是 class。这一篇用平台里的真实例子讲透 OOP,它是看懂和写好 UVM 的前提。
本文要回答的问题
- 句柄(handle)和对象是什么关系,为什么赋值是“共享”不是“复制”?
- 继承和
super怎么复用代码? - 虚方法为什么能让“父类句柄调子类实现”,多态在平台里怎么用?
- 浅拷贝为什么会埋坑,怎么做深拷贝?
一、类与对象:句柄是遥控器
class transaction;
bit [7:0] addr;
bit [7:0] data;
function void print();
$display("addr=%h data=%h", addr, data);
endfunction
endclass
transaction t1, t2;
t1 = new(); // 创建对象,t1 指向它
t2 = t1; // t1、t2 指向同一个对象
t2.addr = 8'hFF;
// t1.addr 也是 FFnew() 才真正分配对象;句柄赋值只复制“遥控器”,两个句柄指向同一对象。函数里传对象默认传句柄,修改对调用方可见——这正是 driver/monitor 共享配置的基础。
二、封装与访问控制
- 默认(无修饰):对外部可见。
local:只有本类能访问,子类也不行。protected:本类和子类可访问,外部不行。
把内部状态设为 protected,通过方法暴露,是降低耦合的常规做法。
三、继承与 super
class bad_trans extends transaction;
bit bad_crc;
function void print();
super.print(); // 先复用父类逻辑
$display("bad_crc=%b", bad_crc);
endfunction
endclassextends 让子类拥有父类全部成员并可扩展或重写;super.new(...) 调用父类构造、super.method() 调用父类方法。构造函数里通常先 super.new() 再做本类初始化。
四、虚方法与多态:平台扩展性的关键
普通方法按句柄声明类型绑定,虚方法按实际对象类型绑定:
class base;
virtual function void kind(); $display("base"); endfunction
endclass
class ext extends base;
virtual function void kind(); $display("ext"); endfunction
endclass
base b;
ext e = new();
b = e;
b.kind(); // 打印 ext(多态);若没有 virtual 则打印 base工程意义:框架里持有父类句柄(如“回调列表”“驱动基类”),用户用子类替换实现,框架无需改动。凡是预期被子类重写的方法(build、run、各种 hook)都应声明 virtual。
抽象类含纯虚方法时不能直接实例化,只用于定义接口契约:
virtual class driver_base;
pure virtual task run();
endclass五、$cast 与类型转换
子类句柄 → 父类句柄可直接赋值;反过来要用 $cast 安全检查:
base b; ext e;
b = new ext();
if (!$cast(e, b)) $fatal("类型不符");$cast(dst, src) 成功返回 1,避免把“其实不是 ext 的对象”硬转后访问非法成员。
六、拷贝:浅拷贝的坑
t2 = new t1; // 浅拷贝:复制标量,但内部对象句柄仍共享浅拷贝复制对象本体,可它包含的子对象句柄仍指向同一处。修改其中一个的嵌套对象会互相串。需要深拷贝时自定义方法递归复制:
function transaction clone();
clone = new();
clone.addr = addr; clone.data = data;
// 若有嵌套对象,此处继续 new 并逐字段复制
endfunction七、对象关系图

八、常见坑
- 忘记
new(),句柄为null,调用方法直接段错误。 - 该用
virtual没用,重写方法不生效,多态退化为父类行为。 - 用
new t1以为是完全复制,嵌套对象仍被两个事务共享。 - 子类构造里没调
super.new(),父类部分未初始化。 - 直接把子类句柄赋反方向而不用
$cast,运行期类型错误。
小结
总结
SystemVerilog OOP 的核心是:new() 才分配对象、句柄赋值只共享"遥控器"而非复制对象;用 local/protected 做封装,用 extends + super 复用父类代码;虚方法按实际对象类型绑定,使框架持父类句柄即可调子类实现(多态),所以 build/run/hook 都应声明 virtual,纯虚抽象类定义接口契约、$cast 做安全向下转型;new t1 只是浅拷贝、嵌套对象句柄仍共享,隔离状态需自定义深拷贝。