Verilog系列:new的初始化顺序

SystemVerilog,类可以认为是一种特殊且具有复杂数据结构的一种类型,其中包含数据成员和一些子程序成员(任务或者函数),数据成员主要用于存储与该类相关的状态信息,子程序一般负责执行赋予数据某些特定意义的操作.在类中,一般将这些数据成员称为类的属性,子程序称为类的方法,两者都是类成员.

类只有经过new()函数构造之后才能真正开辟对应的内存空间,创建对应的实例,才具有具体的意义.否则声明为该类的对象将指向null,不占用任何内存空间.构造函数是类中特殊的成员函数,只要创建class的实例都要执行构造函数,因此类对象具有可以动态创建的特点.构造函数主要作用就是保证每个对象的数据成员具有合适的初始状态,构造函数和其他的函数一样,可以没有形参也可以有多个形参,但是需要注意构造函数不能指定返回值,主要是因为其返回值是一个指向类本身的句柄.在具体使用时,一般可以显式的定义new的函数体也可以隐含的使用new(不声明不定义).

【示例】隐含的使用new函数

【仿真结果】

虽然这里在父类cobj0中没有显式的使用new函数,但是在子类实例化(p.new)的时候还是会首先自动调用父类的隐含new函数,父类中的属性如果没有在new函数中进行初始化将保持其定义时的数值或者默认值,在完成父类初始化后子类中的所有属性将会得到其对应的初值,如果子类中的new函数没有对子类中的属性初始化,那么这些属性将会保持其定义时给定的初值或者属性所属数据类型的默认值.如果子类中的new函数对子类中的属性进行了初始化,那么该属性所具有的最终数值为new函数中指定的数值.这里需要注意,父类中使用隐含new函数时或者无参数的new函数时可以用户手动添加调用父类的new函数(super.new)或者编译器编译时会做自动添加处理,从而可以实现父类中的属性的初始化.

【示例】显式的使用带参数的new函数

【仿真结果】

该示例中,需要注意以下几点:

1、如果基类构造函数new()有参数,那么扩展类必须要有一个构造函数,并且在该构造函数中要执行super.new()调用父类构造函数;

2、cobj1cobj0cobj为继承关系,所以子类可以调用父类中的方法,实例化后的p可以调用其所有父类中的方法,但是不能以super.super.disp的方式调用(这里需要注意并不是所有方法或者数据都可以调用,详情参阅《Verilog系列:localprotected保护类成员》一文)

3、子类在实例化(p=new),其中的属性等并未实现初始化功能,而是先执行父类的初始化操作,super.new()先于子类的初始化.示例中,super.new(d3)执行时因为子类的初始化并没有执行,所以此时的d3并没有初始值,传递给父类new的参数的值为对应被调用new函数参数变量类型的默认值0(此处为int).当通过super.new(d3)执行到cobj0new,因为其是cobj的子类,所以会首先调用其构造函数new中的super.new(),即其父类的new函数.cobj构造时,首先执行cobj中属性的初始化,此时b3被初始化为6,基类属性初始化完毕后,new中的代码将会被执行,此时b3new中被a的值覆盖,即此时b3的值被修改为0.基类cobj中的new执行完毕后返回其子类cobj0.

4、返回子类cobj0首先会初始化cobj0中的属性,然后会执行其中new函数中super.new()之外的其他语句,所以此时虽然cobj0中的c3在属性初始化时初始化为1,但是c3new中又被a覆盖,所以最终c3的值为a的默认值0.cobj0中的new执行完毕后返回其子类cobj1.

5、返回子类cobj1,执行顺序同cobj0,首先会进行属性初始化,完成属性初始化后在进行new中除了super.new()以外的语句的执行,从而完成最终实例的构造.

所以,上例的执行顺如源代码中的数字编号所示,基本流程如下图所示:

通过仿真结果可见,子类在构造时首先执行的是其new函数中的super.new(),即父类的构造函数,在父类的构造函数被调用时,首先会对父类的属性进行初始化,父类属性初始化完毕后,在执行父类中new函数中的相关语句,父类中的new函数执行完毕后返回子类,此时,首先进行的是子类中的属性初始化,然后再执行子类中new函数中除了super.new()以外的语句,从而完成子类的实例化.

因此,如果没有对类的属性进行合理的初始化操作,在对象实例化后得到的某些属性的初始值与期望不一致,所以建议不要在属性声明的同时进行初始化,具体的属性初始化操作均在new函数中进行.

全部评论

相关推荐

牛客717484937号:双飞硕没实习挺要命的
点赞 评论 收藏
分享
评论
2
1
分享
牛客网
牛客企业服务