默认情况下,可以使用类的对象句柄从类外部访问类的成员和方法,即它们是public的。
如果我们不希望某些成员和某些方法可以从类外部访问怎么办?为了防止意外修改类成员/方法。,我们希望将类的成员设置为没有外部类(甚至是其子类)可以访问。
例如在大型项目中,我们可能会使用外部提供的“基”类库(如 UVM )。该第三方基类库需要确保其用户不会意外更改/覆盖类的成员和方法,这会对项目的其余部分产生严重的连锁反应。
封装:将数据隐藏在类中并仅通过类中方法使其可访问的技术称为封装。
换句话说,封装是创建数据容器及对其进行操作的相关方法。
有两种方法可以隐藏类中的数据。它们是通过在类成员前面加上以下关键字来实现的:
local成员在类外不可见,扩展/派生类也不可见。类的本地方法可以访问这些本地成员。一个类的属性和方法都可以是“本地的”。使类成员本地化的关键字是 local。这是一个简单的例子:
class packet; local int addr; //local property local function void disp(input int data); //local method $display("data = %h", data); $display("addr = %h", addr); //access local property endfunction endclass module class_TOP( ); initial begin packet p1; p1 = new( ); // p1.addr = 'hff; //COMPILE ERROR - can't access 'local' property // p1.disp(20); //COMPILE ERROR - can't access 'local' method end endmodule
在“packet”类中,我们定义了一个名为“addr”的“local”属性。由于这是本地的,因此它仅对类中的方法可见。我们还将函数“disp”声明为local方法。它可以访问本地属性“addr”。请注意,这个函数(“disp”)必须是local的才能访问local属性。
让我们举同样的例子,看看是否可以从扩展类访问“local”属性/方法:
class packet; local int addr; local function void disp(input int data); $display("data = %h", data); $display("addr = %h", addr); //access local property endfunction endclass class eth_packet extends packet; function set_addr; //addr = 'hff; //COMPILE ERROR - //can't access 'local' property from extended class endfunction function void eth_disp; //super.disp(50);//COMPILE ERROR - //can't access 'local' method from extended class endfunction function void disp(input int data); //OK to override 'local' method in extended class $display("From eth_packet data=%d", data); endfunction endclass module class_TOP( ); initial begin eth_packet e1; e1 = new( ); e1.disp(50); end endmodule
此示例与上面的示例类似。但在本例中,我们将基类“packet”扩展为类“eth_packet”。在“eth_packet”中,我们尝试访问本地属性“addr”(属于“packet”类),会给我们一个编译错误。同样,当我们尝试访问类“packet”(来自“eth_packet”)的本地方法“disp”时,会得到编译错误。
然后我们在“eth_packet”类中覆盖本地方法“disp”(属于“packet”类)。在子类中覆盖父类的本地方法是可以的。
然后我们从模块“class_TOP”调用“eth_packet”的方法“disp”。
Protected修饰的类属性或方法具有local成员的所有特征,除了它可以被继承,它对扩展/派生类是可见的。
它们可以由扩展的子类访问,但不能从类外部访问。
让我们看一下与上面相同的示例,但将属性声明为“Protected“。我们将看到Protected属性和方法对扩展类是可见的——但在类之外是不可见的。另外,请注意,类中的Protected属性可以通过普通方法访问(即,这些方法不必是“Protected”):
class packet; protected int addr; protected function void disp(input int data); $display("From packet"); $display("\t data = %h", data); $display("\t addr = %h", addr); endfunction endclass class eth_packet extends packet; function set_addr; addr = 'hff; //protected property 'addr' visible to extended class endfunction function void eth_disp; super.disp('hff); //protected method 'disp' visible to extended class endfunction function void disp(input int data); //OK to override 'protected' method in extended class $display("From eth_packet"); $display("\t data = %h", data); endfunction endclass module class_TOP( ); initial begin packet p1; eth_packet e1; e1 = new( ); p1 = new( ); //p1.disp(20); //COMPILE ERROR //protected methods 'disp' not visible outside the class e1.eth_disp; e1.disp('h ffff); end endmodule
在这个例子中,我们在类“packet”中声明了一个Protected属性“addr”和一个Protected方法“disp”。在扩展类“eth_packet”中,我们访问属性“addr”。这没关系,因为基类的Protected属性对其扩展类是可访问/可见的。同样,我们从扩展类“eth_packet”访问方法“disp”(属于“packet”类)。这也可以,因为基类的Protected方法对其扩展类是可访问/可见的。
在模块“class_TOP”中,我们实例化了“packet”和“eth_packet”。但是,当我们尝试从模块“class_TOP”访问“packet”类的“disp”函数时,会出现编译错误。这是因为从类外部看不到Protected方法(或属性)。
最后,使成员成为local或Protected是一种很好的做法。这有助于我们之前讨论的数据隐藏。(封装)
总结问题:
public :公共的,被public关键字修饰的属性或方法,对子类以及外部类都是可见(可访问,调用)的
private(local), 本地的,私有的,被protected关键字修饰的属性或方法,对于子类以及外部类都是不可见的,不能被访问调用的
protected:受保护的,被protected关键字修饰的属性或方法,对于子类是可见的,对于外部类是不可见的。
在SV中,成员变量默认是public