Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
    • Classes, Objects & Handles
      • Class Declaration & Objects
      • Handles vs Objects
      • Constructors & super.new()
      • Shallow vs Deep Copy
      • this, Scope & Nested Classes
    • Inheritance and Polymorphism
    • Static, Encapsulation & Parameterized Classes
    • Virtual Interfaces: the Class-to-Pins Bridge
    • Events, Semaphores, Mailboxes and Processes
    • OOP Patterns for Testbenches
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Classes, Objects & Handles
  1. Topics
  2. SystemVerilog
  3. Object-Oriented SystemVerilog for Verification
  4. Classes, Objects & Handles
  5. this, Scope & Nested Classes

Part 2 · OOP for Verification · Intermediate

this, Scope & Nested Classes

Disambiguate with this, reach class scope with ::, and break cycles with typedef class.

On this page

    Why this matters

    A constructor set every property it was given, and the object still came out empty. The arguments had the same names as the properties, so each assignment wrote the argument to itself.

    After this lesson you can: use this to reach a shadowed property, reach class-scope names with ::, and break a compile cycle with typedef class.

    this names the current object

    Inside a non-static method, this is a handle to the object the method was called on. Its everyday job is disambiguation when an argument shadows a property.

    In a constructor the arguments are conventionally named after the properties they set. The bare name then means the argument, and this.name reaches the property.

    Predict first A constructor is function new(string name); name = name; endfunction, and the class has a property called name. After new("a0"), what does the property hold?

    An empty string, its default. Inside the constructor the bare name means the argument, so name = name assigns the argument to itself. this.name = name is the fix.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class named_l5;
      string name;
      int id;
      function new(string name, int id);
        // bare 'name' is the ARGUMENT; this.name is the property
        this.name = name;
        this.id = id;
      endfunction
      function named_l5 get_self();
        return this; // hand out a reference to myself
      endfunction
    endclass
    
    module tb_l5_this;
      int errors = 0;
      initial begin
        named_l5 a;
        named_l5 g;
        a = new("a0", 3);
        if (a.name != "a0") errors++;
        if (a.id != 3) errors++;
        g = a.get_self();
        if (!(g == a)) errors++;
        if (errors == 0) $display("PASS: this disambiguates shadowed names and returns self");
        else $fatal(1, "FAIL: this and shadowing checks");
        $finish;
      end
    endmodule
    Terminal
    PASS: this disambiguates shadowed names and returns self

    Methods are automatic by default, so each call gets private locals. Object properties stay shared state, so parallel calls can still race on members: keep scratch data in locals, not fields.

    Class scope and the resolution operator

    Name lookup walks outward: method locals, class properties, base classes, then the enclosing scope. The :: operator names an exact class scope from outside it.

    Typedefs, enums, and static members declared inside a class stay there. They are reached with Class::name and never leak out.

    Two words in the bench come from later topics. A static property has one copy shared by every object of the class (Class Features). extends and super make a class that builds on another (Inheritance); here they only show that lookup reaches the base class.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class defs_l5;
      typedef enum {READ, WRITE, IDLE} kind_e;
      typedef bit [31:0] addr_t;
      static int unsigned txn_count = 0;
      function new();
        txn_count++;
      endfunction
    endclass
    
    class base_r;
      int seen = 0;
      function void report();
        seen = 1;
      endfunction
    endclass
    
    class child_r extends base_r;
      function void report();
        super.report(); // nearest parent version runs first
        seen = seen + 10;
      endfunction
    endclass
    
    Terminal
    PASS: class scope lookup and shared counter behave as specified
    The search goes outward and stops at the first match. this, super and :: choose where it starts.

    Cycles need forward declarations

    Two classes holding handles to each other cannot be compiled in either order. A forward declaration typedef class owner_c states the type early, which is enough to declare handles.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    typedef class owner_c; // forward declaration: breaks the cycle
    
    class part_c;
      owner_c parent;
      function new(owner_c parent);
        this.parent = parent;
      endfunction
    endclass
    
    class owner_c;
      part_c prt;
      function void build();
        prt = new(this); // give the part a back-reference to me
      endfunction
    endclass
    
    module tb_l5_cycle;
      int errors = 0;
      initial begin
        owner_c own;
        own = new();
        if (own.prt != null) errors++;
        own.build();
        if (own.prt == null) errors++;
        if (!(own.prt.parent == own)) errors++;
        if (errors == 0) $display("PASS: forward-declared handles link the part back to its owner");
        else $fatal(1, "FAIL: forward declaration checks");
        $finish;
      end
    endmodule
    Terminal
    PASS: forward-declared handles link the part back to its owner

    Helper types with a single user can nest instead. The snippet below keeps the entry type inside its only consumer.

    Need

    Write

    Reached as

    Current object in a method

    this

    A handle value

    Exact class member from outside

    Cls::member

    Statics, typedefs, enums

    A class named before definition

    typedef class C;

    Handle declarations only

    Helper owned by one class

    Nested class

    Outer::Inner

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    // snippet
    class tracker_n;
      class entry; // reached as tracker_n::entry from outside
        int id;
        int unsigned t_in;
      endclass
      entry pending[$];
    endclass

    Common pitfall

    A constructor argument silently shadowing a property assigns argument to argument while the property keeps its default. Using this inside a static method is a compile error because no current object exists.

    Interview

    The scope trio recurs in interviews.

    • When this is required: an argument or local shadows a property; elsewhere it is style.

    • What typedef class is for: forward declaration resolving mutually referencing classes.

    • super.f against Base::f: super starts at the immediate parent; :: names an exact scope.

    Check yourself A constructor argument has the same name as a property. What does a bare assignment set?

    The argument. The property keeps its default unless the assignment uses this.name.

    Check yourself Two classes hold handles to each other. Which line breaks the compile cycle?

    A forward declaration: typedef class owner_c. It lets the first class name the second before its body is seen.

    Remember

    • Inside a method a bare name means the nearest match: argument or local first.

    • Cls::name reaches typedefs, enums and statics declared in a class.

    • typedef class C; lets two classes hold handles to each other.

    Rule of thumb: name the property this.x whenever an argument is also called x.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. Icarus 13 cannot run the ones that compare class handles or keep a queue inside a class.

    Related topics

    • Constructors & super.new()

    • Inheritance & Polymorphism

    Previous

    39 / 259

    Next: Inheritance and Polymorphism

    Up next

    Up nextInheritance and Polymorphism

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Class Declaration & Objects
    • Handles vs Objects
    • Constructors & super.new()