返回介绍

第十一章 类

发布于 2020-09-09 22:55:49 字数 6510 浏览 1028 评论 0 收藏 0

主题
主题描述
11.1 简介SystemVerilog引入了一个面向对象的类数据抽象。类允许对象动态地产生、删除、赋值、以及通过对象句柄访问。对象句柄为语言提供了一种安全的、类似于指针的机制。类提供了继承和抽象建模的能力,这就将不带有任何类型安全性问题的C函数指针的优点引入到了SystemVerilog中,因此,它为Verilog带来了真正的多态性。
11.2 语法
11.3 概述类是一种数据类型,它包含了数据以及对数据进行操作的子程序(函数和任务)。类的数据被称为类属性,它的子程序被称为方法,无论是类属性还是方法都是类的成员。类属性和方法结合在一起,定义了某种类型的对象的内容和能力。

例如,一个数据包可以是一个对象。它可能具有一个命令域、一个地址、一个序列号、一个时戳、以及一个数据包净荷。另外,一个数据包可以实现多项不同的任务:初始化数据包、设置命令、读取数据包的状态、或者检查序列号。尽管每一个数据包都是不同的,然而作为一个类,数据包具有某些固有的属性,这些固有属性可以在类的定义中进行说明。
11.4 对象(类实例)类定义了一个数据类型。对象是类的实例。在使用一个类的时候,需要首先声明一个类型为该类的变量(这个变量保存了一个对象句柄),然后使用new函数产生该类的一个对象,然后再将它赋值给变量。
11.5 对象属性一个对象的数据域可以通过将类属性名限定到一个实例名的方法来使用。继续使用先前的例子,Packet对象p的命令域可以按如下方式使用:
11.6 对象方法我们可以使用与访问类属性相同的语法来访问一个对象的方法:
11.7 构造器SystemVerilog没有要求C++那样的复杂的内存分配和释放。一个对象的构造是直接的,并且碎片收集象Java中那样是隐含的并且是自动的。它没有内存泄漏或其它微妙的行为,而这些往往成为C++程序员的噩梦。

SystemVerilog提供了一种机制在产生对象的时候初始化一个实例。当对象被产生的时候,例如:
11.8 静态类属性前面的例子仅仅声明了实例类属性。每一个类的实例(也就是Packet类型的每一个对象)的每一个变量都有它自己的拷贝。有时要求所有的实例共享变量的一个版本。这些类属性使用关键字static产生。下面的例子演示了一个类的所有实例需要访问一个共用的文件描述符的情况:
11.9 静态方法方法也可以被声明成静态的。一个静态方法遵从于所有的类范围和访问规则,但在表现上它就像一个常规的子例程一样可以在类的外部被调用,即使没有该类的实例。一个静态方法不能访问非静态的成员(类属性和方法),但它可以直接访问静态类属性或调用同一个类的静态方法。在一个静态方法体内部访问非静态成员或访问特殊的this句柄是非法的,并且会导致一个编译错误。静态方法不能是虚拟的。
11.10 thisthis关键字被用来明确地引用当前实例的类属性或类方法。this关键字指示了一个预定义的对象句柄,这个句柄引用了被用来调用this在其中使用的子例程的对象。this关键字应该仅仅使用在非静态的类方法中,否则会发布一个错误。例如,下面的声明是一个用来编写初始化任务的基本方法:
11.11 赋值、重命名与拷贝声明一个类变量仅仅产生一个名字,这个名字表示了一个对象。因此:
11.12 继承与子类前一节定义了一个称为Packet的类。我们可以扩展这个类以便使packet串接在一起形成一个链表。一种解决方案是产生一个新的被称为LinkedPacket的类,它包含一个类型为Packet的变量:packet_c

为了引用Packet类属性,需要引用变量packet_c
11.13 过载成员子类对象也是它们的父类对象的有效表示。例如,每一个LinkPacket对象都是一个完全合法的Packet对象。

一个LinkedPacket对象的句柄可以赋值到一个Packet变量:
11.14 supersuper关键字在一个继承类的内部使用,可以用来引用其父类的成员。当父类成员被继承类成员过载的时候就有必要使用super关键字来访问负类的成员。
11.15 强制类型转换将一个子类变量赋值给层次树中较高的类变量是合法的。将一个超类变量直接赋值给一个子类变量则是非法的。然而,如果超类句柄引用了指定子类的句柄,那么将一个超类句柄赋值给一个子类变量则是合法的。

为了检查赋值是否合法,需要使用动态强制类型转换函数$cast(参见3.15节)。

$cast()的语法如下:
11.16 串接构造器一个子类在实例化的时候会调用类方法new()。在函数中定义的任何代码执行之前,new()执行的一个动作是调用其超类的new()方法,并且会沿着继承树按这种方式一直向上调用。因此,所有的构造器都会按正确的顺序调用,它们都是起始于根基类并结束于当前的类。

如果超类的初始化方法需要参数,那么会有两种选择:一种选择是总是提供相同的参数,另外一种选择是使用super关键字。如果参数总是相同的,那么它们可以在被扩展的时候指定:
11.17 数据隐藏与封装到目前为止,所有的类属性和方法都毫无限制地在类外可见。然而,我们有时希望通过隐藏类属性和类方法的名字来限制在类的外部访问类属性和类方法。这就使得其它程序员能够不依赖于一个特定的实现,而且它还防止仅对类内部有效的类属性被偶然地修改。当所有的数据都变成隐藏的(仅能被公共方法访问),代码的维护和测试都变得更加容易。

在SystemVerilog中,未被限定的类属性和方法是公共的,它们对访问对象名字的任何人都是有效的。

一个被标识成local的成员仅对类内的方法有效。而且这些本地成员在子类内是不可见得。当然,访问本地类属性或方法的非本地方法可以被继承,并且作为子类的方法它可以正确地工作。

除了可以被继承以及对子类可见外,一个被标识成protected的类属性或方法具有本地成员的所有特性。

注意:在类的内部,一个本地方法或类属性可以被引用,即使它处于一个不同的实例中。例如:
11.18 常量类属性与任何其它SystemVerilog变量一样,类属性可以通过const声明成只读的。然而,由于类属性是动态的对象,类属性允许两种形式的只读变量:全局常量和实例常量。

全局常量类属性是那些在声明中包含了初始值得常量类属性。它们与其它的const变量类似,也就是它们不能在除声明之外的其它地方赋值。
11.19 抽象类与虚拟方法我们可以产生一组类,它们可以看作是来源于一个共用的基类。例如,一个类型为BasePacket的公用基类设置了一个包的初始结构,但这个结构并不完整,因此它永远也不会被实例化。然而从这个基类开始,我们可以派生许多子类,例如以太网包、令牌环包、GPSS包、卫星包。这些包的每一个可能看上去很相似,它们都需要相同的方法集合,但它们在内部的实现细节上则存在明显的不同。

一个基类设置了其子类的原型。由于设置基类的目的就不是为了实例化,所以可以将类指定成virtual以便使其变得抽象:
11.20 多态性:动态方法查找多态性使得超类中的一个变量能够保存子类对象,并且能够直接从超类变量中引用这些子类的方法。考虑一个例子,假设Packet对象的基类BasePacket通过虚拟函数定义了通常由它的子类使用的所有公开方法(例如sendreceiveprint等)。这时,尽管BasePacket是抽象的,它仍然可以用来声明一个变量:
11.21 类范围解析操作符 ::类范围操作符 :: 被用来指定在一个类作用范围内定义的一个标识符。它具有下列形式:
11.22 块外声明如果能够将方法定义放在类声明体的外部,那将是很方便的。这个功能可以通过两个步骤完成。首先,在类声明体内声明方法的原型(无论它是任务还是函数)、任何限定符(local、protected或virtual)、以及完整的参数说明加上extern限定符。extern限定符指示方法体(它的实现)在类声明的外部。接着,在类声明的外部声明完整的方法(与原型类似但没有限定符),并将这个方法绑定回它所属的类:使用一对冒号(::)将方法名限定为类名字。
11.23 参数化的类如果定义一个基本类,它的对象在实例化的时候可以具有不同的数组尺寸或数据类型,那么这种功能通常很有用。我们可以不必为每一个尺寸或类型编写相同的代码,并且可以为本质上不同并且不可互换的对象使用同一个说明(这与C++的类模板类似)。

普通的Verilog参数机制可以用来参数化一个类:
11.24 typedef类有时一个类变量需要在类本身被声明之前声明。例如,如果两个类中的每一个类都需要另外一个类的句柄。在编译器处理第一个类的声明期间,编译器遇到了第二个类的引用,而这个引用是未定义的,因此编译器会将它标记为一个错误。

通过使用typedef来为第二个类提供提前声明就可以解决这个问题:
11.25 类与结构体SystemVerilog加入了面向对象的类结构。从表面上看,类与结构体似乎提供了等价的功能,似乎只需要其中的一个就足够了。然而,事实并非如此;类在下述四个基本方面与结构体是不同的:

  1. SystemVerilog结构体是严格的静态对象;它们或者在一个静态的存储位置(全局或模块的作用范围)或者在一个自动任务的堆栈中产生。相反,SystemVerilog对象(也就是类实例)则是动态的,它们的声明不会产生对象,产生对象是通过调用new实现的。
  2. SystemVerilog结构体是类型兼容的,只要它们的位尺寸是相同的,因此拷贝具有不同的构成但尺寸相等的结构体是合法的。相反,SystemVerilog对象具有严格强类型。将一种类型的对象拷贝到另外一个类型的对象是不被允许的。
  3. SystemVerilog对象使用句柄实现,因此它提供了类似于C语言指针的功能。但SystemVerilog不允许为其它类型产生句柄,因此,与C语言不同的是,能够保证SystemVerilog句柄是安全的。
  4. SystemVerilog对象构成了面向对象数据抽象的基础,它提供了真正的多态性。类实例、抽象类、以及动态强制类型转换提供了强大的机制,它远远超过了结构体仅能提供的封装机制。

11.26 内存管理对象、字符串、动态数组、以及联合数组的内存是动态分配的。当创建对象的时候,SystemVerilog会分配更多的内存。当对象不再需要的时候,SystemVerilog自动地释放内存以便这些内存能够被重新使用。自动的内存管理系统是SystemVerilog的整体组成部分。如果没有自动的内存管理,SystemVerilog的多线程、重进入环境会增加许多导致用户出错的机会。一个手工的内存管理系统(就像C语言的malloc和free所提供的那样)是不够的。

例如,考虑下面的例子:
链接 主题

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文