面相对象编程

2023-10-08 11:20
文章标签 对象 编程 面相

本文主要是介绍面相对象编程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

OOP术语

创建对象

静态变量和全局变量

类的方法

继承

蓝图模式

类型向下转换和虚方法

对象的复制

抽象类和虚方法

回调

参数化的类


OOP术语

(1)类(class):包含变量和子程序的基本构建块。Verilog 中与之对应的是模块( rmodule)。

(2)对象(object):类的一个实例。在 Verilog中,你需要实例化一个模块才能使用它。

(3)句柄( handle);指向对象的指针。在 Verilog中,你通过实例名在模块外部引用信号和方法。一个OOP句柄就像一个对象的地址,但是它保存在一个只能指向单一数据类型的指针中。

(4)属性(property):存储数据的变量。在 Verilog中,就是寄存器(reg)或者线网( wire)类型的信号。

(5)方法( method):任务或者函数中操作变量的程序性代码。Verilog模块除了initial和always块以外,还含有任务和函数。

(6)原型( prototype):程序的头。包括程序名、返回类型和参数列表。程序体则包含了执行代码。

创建对象

在声明句柄tr的时候,它被初始化为特殊值null。接下来,调用new ()函数来创建Transaction对象。new函数为Transaction分配空间,将变量初始化为默认值(二值变量为0,四值变量为X),并返回保存对象的地址。对于每一个类来讲,SystemVerilog创建一个默认的new函数来分配并初始化对象。

构造函数

构造函数除了分配内存之外,它还初始化变量。在默认情况下﹐它将变量设置成默认数值——二值变量为0,四值变量为X,等等。可以通过自定义new函数将默认值设成想要的数值。这就是为什么new函数也称为“构造函数”。但是new函数不能有返回值,因为构造函数总是返回一个指向类对象的句柄,其类型就是类本身。

静态变量和全局变量

静态变量

在System Verilog中,可以在类中创建一个静态变量。该变量将被这个类的所有实例所共享,并且它的使用范围仅限于这个类。对于静态变量的访问,无需使用句柄,可以使用类名加上::,即类作用域操作符。

类的方法

类中的方法默认使用自动存储,所以你不必担心忘记使用autonatic修饰符。

继承

当需要的是一种跟原始类很相像的新类,它增加了一些新的变量和方法。继承就可以达到这种效果。继承允许从一个现存的类得到一个新的类并共享其变量和子程序。原始类被称为基类或者超类,而新类因为它扩展了基类的功能,被称为扩展类。继承通过增加新的特性提供了可重用性,并且不需要修改基类,例如对现有的类增加错误注人功能。

通过使用super前缀调用基类中的函数,即super可以调用上一层类的成员,但是SystemVerilog不允许用类似super.super.new的方式进行多层调用,因为这种调用风格跨越了不同的层次,也跨越了不同的边界,自然违反了封装的规则。

应该将类中的子程序定义成虚拟的,这样它们就可以在扩展类中重定义。这一点适用于所有的任务和函数,除了new函数。因为new函数在对象创建时调用﹐所以无法扩展SystemVerilog始终基于句柄类型来调用new函数。

构造函数的规则:如果基类的构造函数有参数,那么扩展类必须有一个构造函数而且必须在其构造函数的第一行调用基类的构造函数。

蓝图模式

OOP中一种常用的技术,当你想要构建一个事务发生器的时候,在验证环境中预留一个父类Transaction的指针,通过子类Transaction继承自父类Transaction来修改Transaction中的约束和变量,在实例化验证平台时,可以根据业务需要实例化父类Transaction还是子类Transaction来调整Transaction的类型,这种技术就称为蓝图模式。可以简单理解为“换汤不换药”,框架(父类指针)不变,实例改变(父类实例或子类实例)。

类型向下转换和虚方法

$cast

类型向下转换或者类型变换是指将一个指向基类的指针转换成一个指向派生类的指针。

使用$cast作类型向下转换。

可以将一个派生类句柄赋值给一个基类句柄,并且不需要任何特殊的代码

当一个类被扩展时,所有的基类变量和方法将被继承。

但是,当你试图做反方向的赋值,即将一个基类对象烤贝到个扩展类的句柄中时,这种操作会失败,因为有些属性仅存在于扩展类中,基类并不具备。

将一个基类句柄赋值给一个扩展类句柄并不总是非法的。当基类句柄确实指向一个派生类对象时是允许的。

$cast子程序会检查句柄所指向的对象类型,而不仅仅检查句柄本身。

$cast(bad2,tr);//检查对象类型并拷贝,如果类型失配则在仿真时报错。如果成功,bad2就指向tr所引用的对象。

if(!$cast(bad2,tr))  //检查类型失配,如果类型失配,在仿真时也不会输出错误信息

        $display("Cant assign tr to bad2");

当将$cast作为一个任务来使用的时候,SystemVerilog会在运行时检查源对象类型,如果跟目的对象类型不匹配则给出一个错误报告。当将$cast作为函数使用时,SystemVerilog仍然做类型检查,但是在失配时不再输出错误信息。如果类型不兼容,$cast函数返回0.如果类型兼容则返回非零值。

虚方法

当需要决定调用哪个虚方法的时候,SystemVerilog根据对象的类型,而非句柄的类型来决定调用什么方法。如果方法没有使用virtual修饰符,SystemVerilog会根据句柄的类型,而不是对象的类型﹐就会导致不是你想要的结果。

OOP中多个子程序使用一个共同的名字的现象叫做“多态( polymorphism)"。它解决了计算机架构设计师面临的一个问题,即如何在物理内存很小的情况下让处理器能够对一个很大的地址空间寻址。

使用虚方法也存在一些劣势——一旦定义了一个虚拟的子程序,所有带有该虚拟子程序的扩展类就必须使用相同的“签名”例如相同类型和个数的参数。在扩展类的虚拟子程序中不能增加或者删除参数。这就意味着你必须提前做好计划。

对象的复制

带有copy虚函数的事务基类中,copy虚函数的返回为基类对象,当扩展基类,生成扩展类时,copy函数仍然需要返回一个基类对象,这是因为扩展类的虚函数必须跟基类的copy虚函数向匹配,包括所有的参数和返回类型。

抽象类和虚方法

OOP语言,例如SystemVerilog,允许你使用两种构造方法来创建一个可以共享的基类。第一种是抽象类,即可以被扩展但是不能被直接实例化的类,它使用“virtual”关键词进行定义。第二种即纯虚(pure virtual)方法,这是一种没有实体的方法原型。一个由抽象类扩展而得来的类只有在所有虚方法都有实体的时候才能被例化。关键词"pure”表明一个方法声明是原型定义,而不仅仅是空的虚方法。最后,纯虚方法只能在抽象类中定义,但是抽象类中也可以定义非纯方法。应当指出的是语言参考手册(LRM)允许你定义一个不带有实体的虚方法----------------你可以调用它但是它会立即返回。

回调

Driver::run任务在无限循环中调用一个transmit任务。在发送事务之前,如果存在前回调(pre_callback)任务,则run进行调用。在发送事务之后,如果存在后回调(postcallback)任务,它也会调用。默认情况下是没有回调任务的,所以run仅仅调用transnit.

你可以将Driver : :run定义为一个虚方法,然后在可能的扩展类MyDriver: : run中覆盖其行为。这样做的缺点是如果你想增加新的行为,可能需要在新方法中重复原方法的所有代码。一旦你对基类作了修改﹐你需要记住将它传播到所有派生类中去。此外,你可以增加一个回调任务而无需修改构成原对象的代码。

一个回调任务应该在顶层测试中创建,在环境中的最低级即驱动器中调用。驱动器无须知道关于测试的任何信息——它只需要使用一个可以在测试中扩展的通用类。驱动器使用一个队列来保存回调对象,这样就可以增加多个对象。回调基类是一个抽象类,使用前必须先进行扩展。

 

需要指出的是虽然Driver_cbs是一个抽象类,但pre_tx和post_tx不是纯虚方法。这是因为一个典型的回调任务只会使用它们中的一个。只要类中存在一个没有实现的纯虚函数,OOP的规则就不允许例化这个类。

参数化的类

例:class generator #(type T=BaseTr);

在建立参数化类的时候,你应当从非参数化类开始,仔细地调试,然后增加参数。这种分开的做法可以减少你之后的调试时间。

宏是参数化类的一种替代形式。例如,可以为发生器定义一个宏,然后用它传递事务数据类型。宏相对于参数化的类来说更难调试。

如果你需要定义若干相关的类使它们共享相同的事务类型,可以使用参数化类或者是一个大的宏。总之.传入类的类型比类的定义更加重要。

你的事务类中的通用虚方法集可以帮助你创建参数化类。例如Generator类使用copy方法,并总是使用相同的签名。类似地,当事务穿过你的测试平台组件时,display方法允许你方便地对它们进行调试。

这篇关于面相对象编程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/164910

相关文章

Java中对象的创建和销毁过程详析

《Java中对象的创建和销毁过程详析》:本文主要介绍Java中对象的创建和销毁过程,对象的创建过程包括类加载检查、内存分配、初始化零值内存、设置对象头和执行init方法,对象的销毁过程由垃圾回收机... 目录前言对象的创建过程1. 类加载检查2China编程. 分配内存3. 初始化零值4. 设置对象头5. 执行

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同

JSON字符串转成java的Map对象详细步骤

《JSON字符串转成java的Map对象详细步骤》:本文主要介绍如何将JSON字符串转换为Java对象的步骤,包括定义Element类、使用Jackson库解析JSON和添加依赖,文中通过代码介绍... 目录步骤 1: 定义 Element 类步骤 2: 使用 Jackson 库解析 jsON步骤 3: 添

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

Java将时间戳转换为Date对象的方法小结

《Java将时间戳转换为Date对象的方法小结》在Java编程中,处理日期和时间是一个常见需求,特别是在处理网络通信或者数据库操作时,本文主要为大家整理了Java中将时间戳转换为Date对象的方法... 目录1. 理解时间戳2. Date 类的构造函数3. 转换示例4. 处理可能的异常5. 考虑时区问题6.

C#反射编程之GetConstructor()方法解读

《C#反射编程之GetConstructor()方法解读》C#中Type类的GetConstructor()方法用于获取指定类型的构造函数,该方法有多个重载版本,可以根据不同的参数获取不同特性的构造函... 目录C# GetConstructor()方法有4个重载以GetConstructor(Type[]