In Inheritance, we saw that methods invoked by a base class handle which points to a child class instance would eventually end up executing the base class method instead of the one in child class. If that function in the base class was declared as virtual
, only then the child class method will be executed.
bc = sc; // Base class handle is pointed to a sub class
bc.display (); // This calls the display() in base class and
// not the sub class as we might think
We'll use the same classes from previous session and do a comparison with and without virtual
function.
Without virtual keyword
// Without declaring display() as virtual
class Packet;
int addr;
function new (int addr);
this.addr = addr;
endfunction
// This is a normal function definition which
// starts with the keyword "function"
function void display ();
$display ("[Base] addr=0x%0h", addr);
endfunction
endclass
module tb;
Packet bc;
ExtPacket sc;
initial begin
sc = new (32'hfeed_feed, 32'h1234_5678);
bc = sc;
bc.display ();
end
endmodule
ncsim> run
[Base] addr=0xfeedfeed
ncsim: *W,RNQUIE: Simulation is complete.
Note that the base class display()
function gets executed.
Inheritance is a concept in OOP that allows us to extend a class to create another class and have access to all the properties and methods of the original parent class from the handle of a new class object. The idea behind this scheme is to allow developers add in new properties and methods into the new class while still maintaining access to the original class members. This allows us to make modifications without touching the base class at all.
Example
ExtPacket is extended and hence is a child class of Packet. Being a child class, it inherits properties and methods from its parent. If there exists a function with the same name in both the parent and child class, then its invocation will depend on the type of the object handle used to call that function. In the example below, both Packet and ExtPacket have a function called display()
. When this function is called by a child class handle, the child class display()
function will be executed. If this function is called by a parent class handle, then the parent class display()
function will be executed.
In a previous post, key topics on class handles and objects were discussed which is essential to understand how shallow copy and deep copy works.
Click here to refresh concepts in class handles and objects !
The this
keyword is used to refer to class properties, parameters and methods of the current instance. It can only be used within non-static methods, constraints and covergroups. this
is basically a pre-defined object handle that refers to the object that was used to invoke the method in which this
is used.
Example
A very common way of using this
is within the initialization block.
class Packet;
bit [31:0] addr;
function new (bit [31:0] addr);
// addr = addr; // Which addr should get assigned ?
this.addr = addr; // addr variable in Packet class should be
// assigned with local variable addr in new()
endfunction
endclass
Unless there is ambiguity in assignment, use of this
keyword is not generally needed for specifying access to class members in methods.
Each class instance would normally have a copy of each of its internal variables.
class Packet;
bit [15:0] addr;
bit [7:0] data;
function new (bit [15:0] ad, bit [7:0] d);
addr = ad;
data = d;
$display ("addr=0x%0h data=0x%0h", addr, data);
endfunction
endclass
module tb;
initial begin
Packet p1, p2, p3;
p1 = new (16'hdead, 8'h12);
p2 = new (16'hface, 8'hab);
p3 = new (16'hcafe, 8'hfc);
end
endmodule
Each of the class objects p1, p2, p3 will have addr and data variables within it.
ncsim> run addr=0xdead data=0x12 addr=0xface data=0xab addr=0xcafe data=0xfc ncsim: *W,RNQUIE: Simulation is complete.