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.
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.
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
endmodulePASS: this disambiguates shadowed names and returns selfMethods 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.
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.
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
PASS: class scope lookup and shared counter behave as specifiedTwo 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.
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
endmodulePASS: forward-declared handles link the part back to its ownerHelper 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 |
| A handle value |
Exact class member from outside |
| Statics, typedefs, enums |
A class named before definition |
| Handle declarations only |
Helper owned by one class | Nested class |
|
// snippet
class tracker_n;
class entry; // reached as tracker_n::entry from outside
int id;
int unsigned t_in;
endclass
entry pending[$];
endclassThe argument. The property keeps its default unless the assignment uses this.name.
A forward declaration: typedef class owner_c. It lets the first class name the second before its body is seen.
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.