Virtual methods in system verilog

Hi I have a confusion regarding virtual methods in system Verilog
this is a code I copied from a blog

class Base;
int unsigned A = 1;
int unsigned B = 2;
function void printA();
$display("Base: A=%0d", A);
endfunction : printA
virtual function void printB();
$display("Base: B=%0d", B);
endfunction : printB
endclass : Base

class Child extends Base;
int unsigned A = 3;
int unsigned B = 4;
function void printA();
$display("Child: A=%0d", A);
endfunction : printA
function void printB();
$display("Child: B=%0d", B);
endfunction : printB
endclass : Child

class Child2 extends Child;
int unsigned A = 5;
int unsigned B = 6;
function void printA();
$display("Child2: A=%0d", A);
endfunction : printA
function void printB();
$display("Child2: B=%0d", B);
endfunction : printB
endclass : Child2

module top();
Base B1;
Child C1;
Child2 C2;
initial begin
B1 = new();
C1 = new();
C2 = new();
B1.printA;// displays 'Base::A is 1'
B1.printB;// displays 'Base::B is 2'
C1.printA;// displays 'Child::A is 3'
C1.printB;// displays 'Child::B is 4'
C2.printA;// displays 'Child2::A is 5'
C2.printB;// displays 'Child2::B is 6'
end
endmodule:top

printA method is not virtual whereas printB is virtual. Since printA is not virual, if I call printA using child class object, I am expecting printA method of the parent class to be excecuted.
Can someone give me a clear idea on this?

In reply to Deepthip:

The type of the class variable alone determines which printA method gets called. Since you override printA in the “child” class, and you use the same class variable type to reference it, the child class method is what gets called.

See my article on OOP inheritance:

Also, see this post about my dislike of parent/child terminology.

In reply to dave_59:

Thanks Dave . I am still confused.But why doesn’t it happen in below code.

typedef enum {IDLE, RUN, P0, P1} cmd_t;

///// Base Class Declaration
class Packet;
 
 /// Properties
 cmd_t cmd;
 int status;
 bit [7:0] data [0:255];
 
 /// Method
 function void SetStatus (input int y);
 status = y;
   $display("base");
 endfunction: SetStatus
 
endclass: Packet

///// Extended Class Declaration
class myPacket extends Packet;
 
 /// Added Properties
 bit errBit;
 
 /// Newly Added Method
 function bit ShowError();
   return(errBit);
 endfunction: ShowError
 
 /// Overriding Method
 function void SetStatus (input int y);
   status = y + 2;
   $display("child");
 endfunction: SetStatus
 
endclass: myPacket

module top;

 Packet pkt = new;
 myPacket m_pkt = new;
 
 task my_run (Packet PKT);

   PKT.SetStatus(2);
   $display("Status value is = %0d", PKT.status); 
   endtask: my_run
 
 initial begin
   my_run(pkt);
   my_run(m_pkt);
 end
 
endmodule: top

and this is my output
base
Status value is = 2
base
Status value is = 2

In reply to Deepthip:

Even in the above snippet I am overriding the method SetStatus in child class and the method is non virtual. But the base class method gets called with both parent and child class handles.
But not the same with first case printA method.

In reply to Deepthip:

In your 2nd code, you have the task(my_run) which has the argument of type base class. So, when you call “my_run(m_pkt)”, in fact, you are passing an object of type derived class to a handle of type base class (like Base_handle = Derived_Object). Hence it will still call base class method since it is non-virtual.

In your 1st code, you are directly calling “derived_object.method()”, hence, the derived class method is getting called.

Please use the code tags.

typedef enum {IDLE, RUN, P0, P1} cmd_t;

class Packet;
  cmd_t cmd;
  int status;
  bit [7:0] data [0:255];

  function void SetStatus (input int y);
    status = y;
    $display("base");
  endfunction: SetStatus
  
endclass: Packet


class myPacket extends Packet;
  bit errBit;

  function bit ShowError();
    return(errBit);
  endfunction: ShowError

  function void SetStatus (input int y);
    status = y + 2;
    $display("child");
  endfunction: SetStatus

endclass: myPacket


module top;
  Packet pkt = new;
  myPacket m_pkt = new;

  task my_run (Packet PKT);
    PKT.SetStatus(2);
    $display("Status value is = %0d", PKT.status); 
  endtask: my_run

  initial begin
    pkt.SetStatus(2);
    $display("Status value is = %0d", pkt.status);
    m_pkt.SetStatus(2);
    $display("Status value is = %0d", m_pkt.status);
    my_run(pkt);
    my_run(m_pkt);
  end
endmodule: top

In reply to karan_18:

Thanks a lot! I got it!