SystemVerilog语法(11)-面向对象编程下篇

文章目录

  • [SystemVerilog OOP 核心进阶:多态、虚方法、深拷贝实战 | UVM底层原理](#SystemVerilog OOP 核心进阶:多态、虚方法、深拷贝实战 | UVM底层原理)
    • [📌 本篇导读](#📌 本篇导读)
    • [1 回顾与衔接](#1 回顾与衔接)
    • [2 虚方法与多态](#2 虚方法与多态)
      • [2.1 为什么需要虚方法](#2.1 为什么需要虚方法)
      • [2.2 重写的黄金规则](#2.2 重写的黄金规则)
      • [2.3 终极陷阱:虚方法的默认参数是编译时绑定的](#2.3 终极陷阱:虚方法的默认参数是编译时绑定的)
      • [2.4 多态数组(验证中常用)](#2.4 多态数组(验证中常用))
    • [3 纯虚方法与抽象类](#3 纯虚方法与抽象类)
    • [4 `final` 关键字:锁定核心逻辑](#4 final 关键字:锁定核心逻辑)
    • [5 `cast()\`:安全的类型转换](#5 `cast()`:安全的类型转换)
    • [6 深拷贝的实现](#6 深拷贝的实现)
      • [6.1 浅拷贝的问题](#6.1 浅拷贝的问题)
      • [6.2 手动实现深拷贝](#6.2 手动实现深拷贝)
      • [6.3 深拷贝覆盖范围](#6.3 深拷贝覆盖范围)
      • [6.4 垃圾回收与 UVM 参考](#6.4 垃圾回收与 UVM 参考)
    • [7 参数化类(模板类)](#7 参数化类(模板类))
    • [8 运算符重载](#8 运算符重载)
    • [9 常量成员与 const 函数](#9 常量成员与 const 函数)
    • [10 类内部的枚举与 typedef](#10 类内部的枚举与 typedef)
    • [11 完整验证示例](#11 完整验证示例)
    • [12 核心概念速查表](#12 核心概念速查表)
    • [13 常见错误与编码建议](#13 常见错误与编码建议)

SystemVerilog OOP 核心进阶:多态、虚方法、深拷贝实战 | UVM底层原理

本文是 SystemVerilog OOP 教程的下篇 ,假设你已经掌握了类、对象、继承、静态成员等基础知识。

进阶篇将深入讲解多态、虚方法、类型转换、参数化类、运算符重载、深拷贝等 UVM 底层核心机制。

学完本文,你将理解 UVM 工厂、phase 机制、sequence 等核心组件的 OOP 原理。


📌 本篇导读

知识点 难度 实战价值 典型场景
虚方法与多态 ⭐⭐⭐⭐ ⭐⭐⭐⭐ 统一接口、动态调用
纯虚方法与抽象类 ⭐⭐⭐ ⭐⭐⭐⭐ 定义接口规范
$cast() 类型转换 ⭐⭐⭐ ⭐⭐⭐⭐⭐ UVM 中每天使用
final 关键字 ⭐⭐ ⭐⭐⭐⭐ 锁定核心逻辑
深拷贝 ⭐⭐⭐ ⭐⭐⭐⭐⭐ 事务复制、避免共享
参数化类 #(type T) ⭐⭐⭐ ⭐⭐⭐⭐ 通用数据结构
运算符重载 == ⭐⭐ ⭐⭐⭐⭐ 比较事务内容
常量与 const 函数 ⭐⭐ ⭐⭐⭐ 配置参数、只读接口
类内枚举与 typedef ⭐⭐ ⭐⭐⭐⭐ 增强封装性

💡 常见误区 :虚方法重写变成重载;虚方法中使用默认参数;$cast 前不检查类型;深拷贝时忘记递归复制子对象;混淆句柄比较与内容比较。

🧭 本文所有内容 【验证】 专用,不可综合


1 回顾与衔接

本文假设你已经掌握以下基础知识(上篇):

  • 类的定义与实例化、构造函数 new()null 句柄检查
  • 属性与方法、外部方法 extern
  • 对象句柄与赋值、浅拷贝概念
  • 静态属性与方法
  • 继承 extendssuper.new()
  • 访问权限 protected / local

如果你对这些概念还不熟悉,建议先阅读上篇:SystemVerilog语法(10)-面向对象编程上篇


2 虚方法与多态

虚方法 :用 virtual 修饰的方法,支持动态绑定(运行时根据对象实际类型调用对应方法)。

2.1 为什么需要虚方法

systemverilog 复制代码
class Base;
    function void print();
        $display("Base");
    endfunction
endclass

class Derived extends Base;
    function void print();
        $display("Derived");
    endfunction
endclass

initial begin
    Base b;
    Derived d = new();
    b = d;           // 父类句柄指向子类对象
    b.print();       // 输出 "Base" ------ 不是我们想要的!
end

因为 print 不是虚方法,编译器根据句柄类型(Base)而非对象实际类型(Derived)来决定调用哪个方法。

解决方案 :在基类方法前加 virtual

systemverilog 复制代码
class Base;
    virtual function void print();
        $display("Base");
    endfunction
endclass

class Derived extends Base;
    virtual function void print();  // 可省略 virtual,但建议保留
        $display("Derived");
    endfunction
endclass

// 此时 b.print() 输出 "Derived" ✅

📌 子类重写虚方法时,可以通过 super.方法名() 调用父类的实现

systemverilog 复制代码
class Derived extends Base;
    virtual function void print();
        super.print(); // 先调用父类的print方法
        $display("Derived"); // 再添加子类的逻辑
    endfunction
endclass

我们可以用一句口诀来记忆: 实方法,看句柄;虚方法,看对象。
💡 多态生效的三个必要条件(缺一不可):

  1. 存在继承关系
  2. 子类重写父类的虚方法
  3. 父类句柄指向子类对象

2.2 重写的黄金规则

⚠️ 子类重写虚方法时,参数类型、数量、顺序、方向以及返回值类型必须与父类完全一致 ,否则会变成方法重载而非重写,多态不会生效。

错误示例(参数类型不同,变成重载)

systemverilog 复制代码
class Base;
    virtual function void f(int a);
        $display("Base.f(int)");
    endfunction
endclass

class Derived extends Base;
    virtual function void f(bit a); // 参数类型不同,变成重载
        $display("Derived.f(bit)");
    endfunction
endclass

initial begin
    Base b = new Derived();
    b.f(1); // 输出 "Base.f(int)",多态失效!
end

错误示例(参数方向不同,变成重载)

systemverilog 复制代码
class Base;
    virtual function void f(input int a);
        $display("Base.f(input int)");
    endfunction
endclass

class Derived extends Base;
    virtual function void f(output int a); // 参数方向不同,变成重载
        $display("Derived.f(output int)");
    endfunction
endclass

initial begin
    Base b = new Derived();
    int x;
    b.f(x); // 输出 "Base.f(input int)",多态失效!
end

2.3 终极陷阱:虚方法的默认参数是编译时绑定的

⚠️ 虚方法的默认参数是编译时绑定的,不会被动态重写!这是 SystemVerilog OOP 中最隐蔽的 bug。

systemverilog 复制代码
class Base;
    virtual function void f(int a = 1);
        $display("Base.f(%0d)", a);
    endfunction
endclass

class Derived extends Base;
    virtual function void f(int a = 2); // 重写默认参数
        $display("Derived.f(%0d)", a);
    endfunction
endclass

initial begin
    Base b = new Derived();
    b.f(); // 输出 "Derived.f(1)" ------ 方法体是子类的,但默认参数是父类的!
end

⚠️ 这是所有主流面向对象语言的通用行为 (包括C++、Java、C#)。默认参数始终是编译时绑定的,基于声明的句柄类型而非运行时的对象类型。很多工程师误以为默认参数会被动态重写,这是最常见的认知错误。

结论:永远不要在虚方法中使用默认参数。如果需要默认值,使用重载方法代替。

2.4 多态数组(验证中常用)

systemverilog 复制代码
// transaction_classes.sv
module polymorphism_demo;

class Transaction;
    int id;
    static int next_id = 0;
    
    function new();
        id = next_id++;
    endfunction
    
    virtual function void display();
        $display("Transaction[%0d]", id);
    endfunction
endclass

class WriteTransaction extends Transaction;
    bit [31:0] addr, data;
    
    function new(bit [31:0] a, bit [31:0] d);
        super.new();
        addr = a;
        data = d;
    endfunction
    
    virtual function void display();
        $display("WRITE[%0d]: addr=0x%h, data=0x%h", id, addr, data);
    endfunction
endclass

class ReadTransaction extends Transaction;
    bit [31:0] addr;
    
    function new(bit [31:0] a);
        super.new();
        addr = a;
    endfunction
    
    virtual function void display();
        $display("READ[%0d]: addr=0x%h", id, addr);
    endfunction
endclass

initial begin
    Transaction tr_q[$];
    WriteTransaction wr = new(32'h1000, 32'hA5);
    ReadTransaction rd = new(32'h2000);
    
    tr_q.push_back(wr);
    tr_q.push_back(rd);
    
    // 打印头和尾
    $display("\n========== Transaction Queue Dump ==========");
    $display("Queue size: %0d", tr_q.size());
    $display("--------------------------------------------");
    
    foreach (tr_q[i])
        tr_q[i].display();  // 多态调用
    
    $display("============================================\n");
    $finish;
end

endmodule

💡 多态的好处:我们可以用一个统一的父类句柄数组来存储所有不同类型的子类对象,然后通过一个统一的接口(display 方法)来操作它们,不需要为每种类型写单独的处理逻辑。


根据你的要求,在保留原文结构的基础上,修正了缺失 pure 关键字的关键错误,并优化了表述的准确性。以下是优化后的版本:


3 纯虚方法与抽象类

pure virtual 声明并省略方法体,表示子类必须实现。含有纯虚方法的类是抽象类,不能直接实例化。

标准语法 :纯虚方法直接以分号结尾,不需要 endfunction

systemverilog 复制代码
abstract class Shape;   // 显式 abstract 关键字(推荐)
    pure virtual function float area(); // 纯虚方法,无方法体
endclass

class Circle extends Shape;
    float radius;
    function float area();
        return 3.14159 * radius * radius;
    endfunction
endclass

抽象类完整规则

  1. 只要包含至少一个纯虚方法,就是抽象类
  2. 抽象类不能直接实例化
  3. 子类必须实现所有继承的纯虚方法,否则子类也会成为抽象类
  4. 抽象类可以包含普通方法、成员变量和构造函数
  5. 纯虚方法不能声明为 extern,必须在类内部直接声明
  6. 纯虚方法不能有默认参数(IEEE 标准明确禁止)
  7. 抽象类的构造函数可以声明为 protected,但不能声明为 local,否则子类无法调用 super.new()

注意 :SystemVerilog 中纯虚方法必须使用 pure virtual 关键字组合,单独 virtual 并省略方法体不会产生纯虚方法。


4 final 关键字:锁定核心逻辑

final 关键字用于禁止进一步的继承或重写,是大型验证项目的安全保障。

用法 含义 UVM 典型场景
final class A; 类不能被继承 封装不可修改的工具类
virtual final function void f(); 方法不能被子类重写 UVM phase 机制
const int WIDTH = 32; 实例级常量,不能修改 定义只读配置参数
static const int WIDTH = 32; 类级常量,所有对象共享 全局配置参数(推荐)

示例

systemverilog 复制代码
class BaseDriver;
    // 锁定驱动主流程,防止子类破坏
    virtual final task run();
        pre_drive();
        drive_transaction();
        post_drive();
    endtask
    
    // 留给子类重写的钩子方法
    virtual protected task pre_drive(); endtask
    virtual protected task drive_transaction(); endtask
    virtual protected task post_drive(); endtask
endclass

5 $cast():安全的类型转换

多态中经常需要在基类句柄和子类句柄之间转换,SystemVerilog 提供 $cast() 函数进行安全的运行时类型检查。

向上转型(安全,自动转换)

systemverilog 复制代码
Transaction tr;
WriteTransaction wr = new();
tr = wr; // 向上转型,安全,自动转换

向下转型(不安全,必须用 $cast()

systemverilog 复制代码
Transaction tr = new WriteTransaction();
WriteTransaction wr;

// ❌ 错误:直接赋值编译错误
wr = tr;

// ✅ 正确:使用 $cast() 进行运行时类型检查
if ($cast(wr, tr)) begin
    $display("转换成功");
    wr.data = 32'hA5;
end else begin
    $error("转换失败,tr 不是 WriteTransaction 类型");
end

⚠️ 标准强制行为$cast 失败时,目标句柄保持其原有值不变 ,不会被自动置为 null。因此,在使用 $cast 前,建议先将目标句柄置为 null,或在失败后明确处理原有值,避免残留旧数据导致逻辑错误。

systemverilog 复制代码
// 推荐写法
WriteTransaction wr = null;
if ($cast(wr, tr)) begin
    // 转换成功,使用 wr
end else begin
    $error("类型不匹配");
    // 此时 wr 仍为 null,安全
end

UVM 典型场景

systemverilog 复制代码
uvm_sequence_item item;
my_transaction tr;
$cast(tr, item); // UVM 中最常用的操作之一

6 深拷贝的实现

⚠️ new() 执行的是浅拷贝。对于包含动态数组、句柄成员的对象,必须手动实现深拷贝。

6.1 浅拷贝的问题

systemverilog 复制代码
Packet p1 = new();
p1.payload = new[4];
p1.payload[0] = 8'hAA;

Packet p2 = new(p1);   // 浅拷贝
p2.payload[0] = 8'hFF;

$display("%h", p1.payload[0]);  // 输出 FF ------ 被意外修改了!

原因 :动态数组变量本身是句柄,浅拷贝只复制句柄,导致两个对象的 payload 指向同一块堆内存。

6.2 手动实现深拷贝

深拷贝的目标:递归复制所有成员,创建完全独立的副本。

systemverilog 复制代码
class Header;
    int length;
    function Header deep_copy();
        Header c = new();
        c.length = this.length;
        return c;
    endfunction
endclass

class Packet;
    Header hdr;            // 对象句柄
    bit [7:0] payload[];   // 动态数组
    
    function Packet deep_copy();
        Packet c = new();
        // 递归复制子对象(判空)
        c.hdr = (this.hdr != null) ? this.hdr.deep_copy() : null;
        // 重新分配动态数组并复制元素
        c.payload = new[this.payload.size()];
        c.payload = this.payload;
        return c;
    endfunction
endclass

6.3 深拷贝覆盖范围

数据类型 处理方式
基本类型(int, bit 等) 值复制(自动)
静态数组 自动复制所有元素
动态数组/队列 重新 new[size] + 元素复制
对象句柄 递归调用子对象的 deep_copy()
邮箱/信号量 通常不复制(破坏通信语义)
静态成员 不复制(属于类本身)

6.4 垃圾回收与 UVM 参考

  • 垃圾回收:SystemVerilog 自动回收无句柄指向的对象,无需手动释放内存。
  • UVM 中的深拷贝uvm_object 提供 clone()copy() 方法,原理与本节的 deep_copy 一致,UVM 用户应优先使用 clone()

使用示例

systemverilog 复制代码
Packet p1 = new();
p1.payload = new[4];
p1.payload[0] = 8'hAA;
Packet p2 = p1.deep_copy();
p2.payload[0] = 8'hFF;
$display("%h", p1.payload[0]); // 输出 AA,独立

关键要点:深拷贝必须处理判空、动态数组需重新分配、子对象需递归调用。静态成员不参与复制。


7 参数化类(模板类)

使用 #(type T) 定义类,实现通用容器。

systemverilog 复制代码
class Stack #(type T = int);
    protected T items [$];
    
    function void push(T t);
        items.push_back(t);
    endfunction
    
    function T pop();
        if (items.size() == 0) begin
            $error("Stack empty");
            return null;
        end
        return items.pop_back();
    endfunction
    
    function int size();
        return items.size();
    endfunction
endclass

initial begin
    Stack #(string) str_stack = new();
    str_stack.push("Hello");
    str_stack.push("World");
    $display("%s", str_stack.pop());  // World
    
    Stack #(Transaction) tr_stack = new();
    tr_stack.push(new());
end

注意:不同特化的参数化类是完全独立的类,静态成员不共享。
📌 参数化类支持多个类型参数和值参数

systemverilog 复制代码
class FIFO #(type T = int, int DEPTH = 1024);
    protected T mem [DEPTH];
    // ...
endclass

使用 typedef 简化

systemverilog 复制代码
typedef Stack#(Transaction) TransactionStack;
TransactionStack tr_stack = new(); // 无需每次都写参数

参数化类的继承(UVM 中最常用的写法):

systemverilog 复制代码
class TransactionStack extends Stack #(Transaction);
    function void push_back(Transaction t);
        push(t);
    endfunction
endclass

8 运算符重载

重载运算符可以让对象的操作更直观,验证中最常用的是重载 == 运算符来比较两个事务的内容是否相等。

📌 默认情况下,===== 对于句柄的作用完全相同 。因为句柄是二值类型(只有 01 两种状态,null 等价于 0),不存在 xz 状态,所以两种比较运算符没有区别。

标准建议 :使用 const ref 引用传递。

systemverilog 复制代码
class Transaction;
    bit [31:0] addr;
    bit [31:0] data;
    bit [7:0]  payload [];

    function bit operator==(const ref Transaction rhs);
        if (rhs == null) return 0;
        if (this.addr != rhs.addr) return 0;
        if (this.data != rhs.data) return 0;
        if (this.payload.size() != rhs.payload.size()) return 0;
        foreach (this.payload[i])
            if (this.payload[i] != rhs.payload[i]) return 0;
        return 1;
    endfunction
endclass

initial begin
    Transaction tr1 = new(), tr2 = new();
    tr1.addr = 32'h1000; tr2.addr = 32'h1000;
    if (tr1 == tr2) $display("内容相等");
end

⚠️ 重要提示 :SystemVerilog 不会自动根据重载的 == 生成对应的 != 运算符 。如果需要使用 != 比较对象内容,有两种方式:

  1. 手动重载 != 运算符
  2. 在代码中使用 !(a == b)(推荐,避免重复代码)

9 常量成员与 const 函数

systemverilog 复制代码
class Config;
    const int BUS_WIDTH = 32;          // 常量成员,不可修改
    
    const bit [31:0] BASE_ADDR;        // 实例常量,可在构造函数中赋值
    
    function new(bit [31:0] base);
        BASE_ADDR = base;              // 常量只能在构造函数中初始化一次
    endfunction
    
    // const 成员函数:承诺不修改非静态成员
    virtual function void display() const;
        access_count++; // 正确:可以修改静态成员
        // BUS_WIDTH = 64; // 错误:不能修改非静态 const 成员
        $display("Bus width = %0d, base = 0x%h", BUS_WIDTH, BASE_ADDR);
    endfunction
endclass

⚠️ const 函数限制

  • const 函数只能调用其他 const 函数,不能调用非 const 函数,否则会编译错误------因为非 const 函数可能修改类成员,违反 const 的承诺。
  • const 函数承诺不修改任何非静态 成员变量。它可以修改静态成员变量,因为静态成员属于类本身,不属于任何特定对象。

10 类内部的枚举与 typedef

systemverilog 复制代码
class Packet;
    typedef enum { GOOD, BAD, CORRUPTED } pkt_type_e;
    pkt_type_e pkt_type;
    
    function void set_type(pkt_type_e _type);
        this.pkt_type = _type;
    endfunction
    
    function void display();
        $display("Packet type = %s", pkt_type.name());
    endfunction
endclass

initial begin
    Packet p = new();
    p.set_type(Packet::GOOD);   // 通过类作用域访问
    p.display();
end

11 完整验证示例

下面是一个综合运用虚方法、深拷贝、参数化类、运算符重载以及多态驱动器的完整示例,可直接编译运行。

systemverilog 复制代码
//===========================================================
// 文件名: oop_advanced_demo.sv
// 功能: 多态驱动器 + 深拷贝 + 参数化栈
// 运行: vcs -sverilog oop_advanced_demo.sv -R
//===========================================================

// 基础事务类(支持多态)
class Transaction;
    int id;
    static int next_id = 0;
    
    function new();
        id = next_id++;
    endfunction
    
    virtual function void display();
        $display("Transaction[%0d]", id);
    endfunction
endclass

class WriteTransaction extends Transaction;
    bit [31:0] addr, data;
    
    function new(bit [31:0] a, bit [31:0] d);
        super.new();
        addr = a;
        data = d;
    endfunction
    
    virtual function void display();
        $display("WRITE[%0d]: addr=0x%h, data=0x%h", id, addr, data);
    endfunction
endclass

class ReadTransaction extends Transaction;
    bit [31:0] addr;
    
    function new(bit [31:0] a);
        super.new();
        addr = a;
    endfunction
    
    virtual function void display();
        $display("READ[%0d]: addr=0x%h", id, addr);
    endfunction
endclass

// 驱动器基类与派生类(多态)
class Driver;
    virtual function void send(Transaction t);
        $display("Driver sending:");
        t.display();
    endfunction
endclass

class AxiDriver extends Driver;
    virtual function void send(Transaction t);
        $display("AXI starting...");
        super.send(t);
        $display("AXI done.");
    endfunction
endclass

// 深拷贝示例类
class Header;
    int length;
    function Header deep_copy();
        Header c = new();
        c.length = this.length;
        return c;
    endfunction
endclass

class Packet;
    Header hdr;
    bit [7:0] payload [];
    
    // 深拷贝函数
    function Packet deep_copy();
        Packet c = new();
        c.hdr = (this.hdr != null) ? this.hdr.deep_copy() : null;
        c.payload = new[this.payload.size()];
        c.payload = this.payload;
        return c;
    endfunction
    
    // 改用普通equals函数(VCS 2016 不支持 operator==)
    function bit equals(Packet rhs);
        if (rhs == null) return 0;
        if (payload.size() != rhs.payload.size()) return 0;
        foreach (payload[i])
            if (payload[i] != rhs.payload[i]) return 0;
        return 1;
    endfunction
endclass

// 参数化类
class Stack #(type T = int);
    protected T items[$];
    function void push(T t); items.push_back(t); endfunction
    function T pop();
        T tmp;
        if (items.size() == 0) begin $error("Empty"); return tmp; end
        return items.pop_back();
    endfunction
    function int size(); return items.size(); endfunction
endclass

// 测试模块
module tb_advanced;
    initial begin
        // 直接用类名声明变量
        AxiDriver drv;
        WriteTransaction wr;
        ReadTransaction rd;
        Stack #(string) s;
        Packet p1, p2;
        Stack #(Packet) ps;  // 直接参数化

        // ----- 多态驱动器演示 -----
        $display("\n=== 多态驱动器 ===");
        drv = new();
        wr = new(32'h1000, 32'hA5);
        rd = new(32'h2000);
        
        drv.send(wr);
        drv.send(rd);

        // ----- 参数化类演示 -----
        $display("\n=== 参数化类 ===");
        s = new();
        s.push("Hello");
        s.push("World");
        $display("Pop: %s", s.pop());

        // ----- 深拷贝与比较演示 -----
        $display("\n=== 深拷贝与比较 ===");
        p1 = new();
        p1.payload = new[2];
        p1.payload[0] = 8'hAA;
        p2 = p1.deep_copy();
        $display("p1 equals p2 ? %b", p1.equals(p2));

        // ----- 参数化类演示 -----
        $display("\n=== 参数化类栈 ===");
        ps = new();
        ps.push(p1);
        $display("Stack size: %0d", ps.size());
        
        $finish;
    end
endmodule

运行输出示例


以下是根据您的要求,删除第14节后优化并更正的内容:


12 核心概念速查表

概念 语法 作用
虚方法 virtual function ... 运行时动态绑定,实现多态
纯虚方法 pure virtual function ...(); 定义接口,强制子类实现
抽象类 virtual class ... 不能实例化,只能作为基类
final 类 final class ... 禁止继承
向下转型 $cast(sub, base) 安全类型转换,运行时检查
深拷贝 手动实现 deep_copy() 递归复制所有成员
参数化类 class #(type T) 通用数据结构模板
运算符重载 virtual function bit operator==(ref class_type rhs); 自定义对象内容比较

13 常见错误与编码建议

常见错误清单

  1. 虚方法重写变成重载 → 检查参数签名(类型、数量、顺序、方向、返回值)是否完全一致。
  2. 虚方法中使用默认参数 → 永远不要使用。
  3. 忘记 $cast 直接向下转型 → 编译错误。使用 $cast 并检查返回值。
  4. 深拷贝时忘记递归复制子对象 → 子对象仍是共享引用。
  5. 混淆句柄比较和对象内容比较 → 重载 == 或实现 compare() 方法。
  6. 抽象类直接实例化 → 编译错误。
  7. 在类中使用 initial → 初始化放在 new() 中。
  8. $cast 失败后未处理目标句柄 → 目标句柄保持原值,建议提前置 null
  9. 抽象类构造函数声明为 local → 子类无法调用 super.new(),应使用 protected

编码规范建议

  • 所有需要多态的方法都声明为 virtual
  • 抽象类显式使用 virtual class 关键字(符合 IEEE 标准,不可使用 abstract
  • 深拷贝必须检查子对象 null
  • 全局配置参数使用 static const 定义
  • 子类重写虚方法时保留 virtual 关键字
  • 使用 typedef 简化参数化类的重复实例化
  • 优先使用 !(a == b) 而非重载 !=,避免代码重复

📢 关于文章 :原创实战分享,转载需注明出处。

📚 参考文档IEEE Standard for SystemVerilog

相关推荐
ALINX技术博客3 小时前
【黑金云课堂】FPGA技术教程FPGA基础:FIFO与Uart通信
fpga开发·uart·fpga·fifo
Ether IC Verifier4 天前
SystemVerilog 数据类型详解
php·systemverilog·uvm·ic验证
南檐巷上学4 天前
基于Zynq-7020的带有正弦波发生器的8051软核设计
单片机·嵌入式硬件·fpga开发·fpga
ALINX技术博客5 天前
【FPGA 开发教程】基于 ALINX FPGA 开发板实现 USB3.2 高速通信(Z7-P+FL2010)
fpga开发·fpga·fmc子卡·usb3.2通信
Yolanda945 天前
【编程学习】复盘经典 VB OOP 示例:推翻旧认知,重学面向对象
java·面向对象
宇哥啊7 天前
UVM中virtual关键字的用法详解
fpga
夜幕下的灯火7 天前
基于 FPGA 的 SD 卡音频播放与电子琴系统
fpga开发·毕业设计·课程设计·fpga·altera
Ether IC Verifier8 天前
IC 验证工程师新手入门指南
systemverilog·ic验证·dpu
寰宇的行者8 天前
软件架构风格之调用返回风格:主程序、面向对象、分层,到底怎么区分?
软考·面向对象·软件架构·分层架构·调用返回风格