类的继承
SystemVerilog支持单继承(类似Java,而不像C++). 有一个让SystemVerilog支持多重继承的提案[1], 但是短期内不会看到曙光。
目录
什么是继承?
继承是面向对象编程范式的关键概念。类用来创建用户自定义类型. 继承使得用户可以用非常安全,非侵入的方式对类的行为进行增加或者修改。使用继承可以定义子类型,在子类型中增加新的方法和数据。被继承的类一般称为基类(SystemVerilog中的超类),得到的新类一般称为引申类(或子类)。
为什么继承如此重要? 因为它使得复用得以实现。让我们通过实例来说明. 假设我们对一个图像模块进行建模. 对其中一部分,我们写了一个代表颜色的类:
class Color;
byte unsigned red;
byte unsigned green;
byte unsigned blue;
function new(byte unsigned red_ = 255,
byte unsigned green_ = 255,
byte unsigned blue_ = 255);
red = red_;
green = green_;
blue = blue_;
endfunction: new
function mix(Color other);
function brighter(float percent);
task draw_pixel(int x, int y);
Now现在它的下一个版本希望能够处理部分透明的图像。为此,我们给Color类增加了一个alpha成员,。alpha代表图像的透明度。alpha越大,图像的像素越结实(不透明)。'0'代表完全透明,使得图片的背景全部可见。因此,我们修改color类如下:
class Color;
byte unsigned red;
byte unsigned green;
byte unsigned blue;
byte unsigned alpha;
function new(byte unsigned red_ = 255,
byte unsigned green_ = 255,
byte unsigned blue_ = 255,
byte unsigned alpha_ = 255);
red = red_;
green = green_;
blue = blue_;
alpha = alpha_;
endfunction: new
function mix(Color other); // new implementation -- would depend on
// alpha values for both the colors
function brighter(float percent); // original implementation good enough
task draw_pixel(int x, int y); // new implementation
// Other functions ...
endclass: Color
注意,即使许多代码是由之前版本的Color类复制而来,我们还是需要单独维护两个版本的代码。这时继承就可以发挥作用,使用继承,我们可以简单的从原始的Color类继承出新类,来添加alpha成员。
class ColorWithAlpha extends Color;
byte unsigned alpha;
function new(byte unsigned red_ = 255,
byte unsigned green_ = 255,
byte unsigned blue_ = 255,
byte unsigned alpha_ = 255);
super.new(red_, green_, blue_);
alpha = alpha_;
endfunction: new
function mix(Color other); // new implementation -- would depend on
// alpha values for both the colors
task draw_pixel(int x, int y); // new implementation
// Other functions ...
endclass: Color
这里我们使用关键字"extend" 来创建一个新类ColorWithAlpha. 注意到我们仅仅需要声明新增加的alpha数据成员。其他成员作为超类对象的一部分用来表示原始的Color类。
有C++背景的用户会注意到在如何访问原始Color类成员的方式,SystemVerilog和C++很不一样。在C++中,原始类(基类)的数据成员的就像本来也就属于继承类的成员一样;在SystemVerilog中,需要额外一层的间接访问,super指定的超类对象和继承类被看作不同的对象。但是,如果需要在继承类中去访问基类成员,SystemVerilog编译器会隐式的帮助完成这部分间接访问。(译者补充:不需要用super去限定,编译器帮忙做这个事情)
ColorWithAlpha color1;
color1 = new(127, 127, 127, 255); // solid gray50
color1.brighter(50); // make 50% brighter
if (color1.red == 127) begin
// some code
// ..
end
顺便说一声, 将super作为一个不同的对象,也就要求SystemVerilog将super放在不同的分开的存储块(和扩展类的存储块不连续)因此,在编译器层来看,实现C++风格的多重继承就变得更加困难。这也就是为什么提案中的SystemVerilog多重继承实现方式采用了java语言的方法。
有什么好处
正如上面例子所示,继承时我们容易扩展类,使得基类复用。复用以至于方便写库,库本身就是一系列的复用组件。
在过程式语言编程中,编写库很简单,仅仅需要写一系列的函数,用户只需要直接调用这些函数即可。 如果用户需要某些方面的定制,一般是通过定制变量以及钩子(回调函数)来完成。 面向对象的库不一样,一个面向对象库一般需要提供一系列的类(抽象的或者具体的类)。许多部分可以直接拿来使用,但是更多的情况是,用户需要根据自己需要扩展这些库中的类。继承就提供了一个很重要的扩展方式。
下面给出面向对象程序设计中最重要的原则。
【【根据译者的理解进行补充:
回调函数的概念是指预先定义一些需要调用的函数原型,缺省不执行任何动作,用户可以覆盖此函数,通过将调用此函数的函数指针覆盖,使得之前的代码不变的情况下,调用函数的内容发生变化,达到定制要求,这个在面向对象中也有用到,比如Callback函数
】】
开-关定律
Dr Bertrand Meyer 由于发明了“开-关”定律而享有盛誉。 此定律被认为是面向对象编程中最重要的原则。简而言之,此定律告诉我们写模块的时候应该使得模块在不做修改的情况下很容易扩展。此定律更正式的表述如下[2]:
软件单元(类,模块,函数,等等)应该对扩展开放,但是对修改关闭。