动态对象与数据的生命周期
- 来源: https://www.freebasic.net/wiki/wikka.php?wakka=ProPgObjectLifetime
- 最后更新: 2019-09-01
使用动态内存分配声明关键字创建的动态对象及其数据的生命周期。
前言:
- 对象(及其数据)的生命周期是其标识符变量存在(且引用有效数据)的时间段。但从绝对意义上说,对象的标识符变量及其关联数据可以有两个独立的生命周期(作用域指标识符变量可见的程序部分)。
- 所考虑的动态对象包括预定义伪对象,如可变长度字符串/数组,以及具有自身动态分配数据的复杂 UDT 实例。
- 也考虑以动态方式分配的简单变量,以及同样以动态方式分配的动态对象。
- 动态内存分配的声明关键字有:
Allocate/Callocate/Reallocate、New、ImageCreate(释放对应:Deallocate、Delete、ImageDestroy)。
对于上述定义的动态分配的对象和数据,对象标识符变量的生命周期通常与周围的作用域相匹配(否则可以大于该作用域),但关联数据的生命周期可能与之不一致,因为关联数据的分配/释放是由用户自身触发的。
用户以静态方式分配的预定义伪对象
即使这些预定义类型变量(可变长度字符串 (1) 或可变长度数组 (2))以如下静态方式分配(或类似语法):
(1)Dim [Shared] As String stringname ...
或
(2)Dim [Shared] As datatype arrayname() ...
这些变量也可以被视为动态伪对象,因为它们是两个实体的组合:
与标识符变量关联的描述符(stringname
(1)或 arrayname()(2)),即第一个实体引用内存中的动态分配(字符串数据
(1)或数组数据(2)),即第二个实体(无名称)。
如果使用了 Shared,描述符分配在 .BSS 或 .DATA 段中,否则分配在程序栈上。
字符串数据通过字符串赋值在堆中分配/重新分配/释放,同时相应地更新描述符(赋空字符串不会销毁描述符,只是重置它)。
数组数据通过 Redim 在堆中分配/重新分配,通过 Erase 释放,同时相应地更新描述符(Erase 不销毁描述符,只是重新初始化它)。
因此,无论应用何种用户命令,标识符变量始终在其作用域内保持定义,而内存分配可以在同一作用域内动态修改/释放(根据用户命令)。
用户以静态方式分配的动态对象
用户也可以通过具有成员过程的复杂 UDT 来定义动态对象,这些成员过程用于分配/重新分配/释放与其关联的动态数据。
通常用于执行此操作的成员过程是构造函数(用于分配)、赋值运算符(用于重新分配)和析构函数(用于释放)。
即使对象标识符变量以静态方式分配(与上面类似):
Dim [Shared] As complexUDT instancename ...
这会通过隐式调用 UDT 构造函数然后析构函数,自动按照标识符变量的作用域来分配和释放对象数据,但在此期间,动态数据分配可能会受到用户命令的深刻影响(例如对 UDT 重载运算符的显式调用)。
用户以动态方式分配的简单变量
关键字(Allocate、Reallocate、New、ImageCreate)用于声明动态分配,创建一个无名实体,其生命周期取决于其他用户命令(Deallocate、Delete、ImageDestroy)。
通常,这些分配关键字包含在用于初始化 (1|3) 或赋值 (2|4) 简单变量(指针 (1|2) 或引用 (3|4))的表达式中,例如:
(1)Dim As datatype Ptr DATApointername = New datatype ...
或
(2)Dim [Shared] As datatype Ptr DATApointername
(2).....
(2)DATApointername = New datatype ...
或
(3)Dim Byref As datatype DATAreferencename = *New datatype ...
或
(4)Dim [Shared] Byref As datatype DATAreferencename = *Cptr(datatype Ptr, 0)
(4).....
(4)@DATAreferencename = New datatype ...
因此,在这种情况下,存在两个不同的实体:
有名指针
(1|2)或引用(3|4),即第一个实体,指向
(1|2)或引用(3|4)已分配内存的,即第二个实体(无名称)。
不要混淆这两个实体,每个实体都有自己的生命周期。
Deallocate、Delete、ImageDestroy 只释放第二个实体(不释放第一个),例如使用:
(1|2)Delete DATApointername
或
(3|4)Delete @DATAreferencename
用户也以动态方式分配的动态对象
动态对象(复杂 UDT)也可以以动态方式分配(与上面类似),通过初始化 (1|3) 或赋值 (2|4) 简单变量(指针 (1|2) 或引用 (3|4)),例如:
(1)Dim As complexUDT Ptr UDTpointername = New complexUDT ...
或
(2)Dim [Shared] As complexUDT Ptr UDTpointername
(2).....
(2)UDTpointername = New complexUDT ...
或
(3)Dim Byref As complexUDT UDTreferencename = *New complexUDT ...
或
(4)Dim [Shared] Byref As complexUDT UDTreferencename = *Cptr(complexUDT Ptr, 0)
(4).....
(4)@UDTreferencename = New complexUDT ...
因此,在最后这种情况下,可以考虑三个实体:
有名指针
(1|2)或引用(3|4),即第一个实体,指向
(1|2)或引用(3|4)已分配的对象字段,即第二个实体(无名称),以及寻址动态分配的关联数据,即第三个实体(无名称)。
不要混淆这三个实体,每个实体都有自己的生命周期。
Delete 释放第二个实体(不释放第一个),它首先开始释放第三个实体(通过调用其析构函数),例如使用:
(1|2)Delete UDTpointername
或
(3|4)Delete @UDTreferencename
示例
用户也以动态方式分配的动态对象(复杂 UDT):
第一个实体:UDT 引用,静态分配在程序栈上,
第二个实体:UDT 实例(包含其 zstring 指针字段)(由 UDT 引用引用),由用户在堆中动态分配,
第三个实体:zstring 数据(由 zstring 指针字段引用),由 UDT 过程成员(构造函数、let 运算符、析构函数)在堆中动态重新分配。
start GeSHi
Type complexUDT
Public:
Declare Constructor ()
Declare Constructor (ByVal p As ZString Ptr)
Declare Operator Let (ByVal p As ZString Ptr)
Declare Operator Cast () As String
Declare Property info () As String ' allocation address, allocation size, string length
Declare Destructor ()
Private:
Dim As ZString Ptr pz
End Type
Declare Sub prntInfo_printString (ByRef u As complexUDT)
Print "'Dim Byref As complexUDT ref = *New complexUDT(""Beginning"")':"
Dim ByRef As complexUDT ref = *New complexUDT("Beginning")
prntInfo_printString(ref)
Print "'ref = """"':"
ref = ""
prntInfo_printString(ref)
Print "'ref = ""FreeBASIC""':"
ref = "FreeBASIC"
prntInfo_printString(ref)
Print "'ref = ""Programmer's Guide / Declarations / Dynamic Object and Data Lifetime""':"
ref = "Programmer's Guide / Declarations / Dynamic Object and Data Lifetime"
prntInfo_printString(ref)
Print "'ref.Destructor()':"
ref.Destructor()
prntInfo_printString(ref)
Print "'ref.Constructor()':"
ref.Constructor()
prntInfo_printString(ref)
Print "'ref.Constructor(""End"")':"
ref.Constructor("End")
prntInfo_printString(ref)
Print "'Delete @ref':"
Delete @ref
@ref = 0 ' systematic safety to avoid double-delete on same allocation
Sleep
Constructor complexUDT ()
Print " complexUDT.Constructor()"
This.pz = Reallocate(This.pz, 1)
(*This.pz)[0] = 0
End Constructor
Constructor complexUDT (ByVal p As ZString Ptr)
Print " complexUDT.Constructor(Byval As Zstring Ptr)"
This.pz = Reallocate(This.pz, Len(*p) + 1)
*This.pz = *p
End Constructor
Operator complexUDT.Let (ByVal p As ZString Ptr)
Print " complexUDT.Let(Byval As Zstring Ptr)"
This.pz = Reallocate(This.pz, Len(*p) + 1)
*This.pz = *p
End Operator
Operator complexUDT.Cast () As String
Return """" & *This.pz & """"
End Operator
Property complexUDT.info () As String
Return "&h" & Hex(This.pz, SizeOf(Any Ptr) * 2) & ", " & _ ' allocation address
Len(*This.pz) + Sgn(Cast(Integer, This.pz)) & ", " & _ ' allocation size
Len(*This.pz) ' string length
End Property
Destructor complexUDT ()
Print " complexUDT.Destructor()"
This.pz = Reallocate(This.pz, 0)
End Destructor
Sub prntInfo_printString (ByRef u As complexUDT)
Print " " & u.info
Print " " & u
Print
End Subend GeSHi
输出:
'Dim Byref As complexUDT ref = *New complexUDT("Beginning")':
complexUDT.Constructor(Byval As Zstring Ptr)
&h001F2AD0, 10, 9
"Beginning"
'ref = ""':
complexUDT.Let(Byval As Zstring Ptr)
&h001F2AD0, 1, 0
""
'ref = "FreeBASIC"':
complexUDT.Let(Byval As Zstring Ptr)
&h001F2AD0, 10, 9
"FreeBASIC"
'ref = "Programmer's Guide / Declarations / Dynamic Object and Data Lifetime"':
complexUDT.Let(Byval As Zstring Ptr)
&h001F2AD0, 69, 68
"Programmer's Guide / Declarations / Dynamic Object and Data Lifetime"
'ref.Destructor()':
complexUDT.Destructor()
&h00000000, 0, 0
""
'ref.Constructor()':
complexUDT.Constructor()
&h001F2AD0, 1, 0
""
'ref.Constructor("End")':
complexUDT.Constructor(Byval As Zstring Ptr)
&h001F2AE0, 4, 3
"End"
'Delete @ref':
complexUDT.Destructor()参见
Allocate,Callocate,Reallocate,DeallocateNew (Expression),Delete (Statement)ImageCreate,ImageDestroyRedim,Erase- 存储类
- 变量作用域
- 简单变量的生命周期与作用域
返回 目录