Constructors, '=' Atsignment-Operators, rnd Destructors (advanced, parts#1) |
Top Previous Next |
Constructors, '=' Assignment-Operators, and Destructors (advanced, part #1) Proper use of Constructors, '=' Assignment-Operators, and Destructors, which are the special member procedures for constructing/initializing, assigning, and destroying objects (part #1). Preamble:
Some member procedures, which have a predefined name in the Type, have a specific role that is constructing, assigning, and destroying objects. These are the constructors, the '=' assignment-operators, and the destructor. Of these, some are special because a version will be automatically built by the compiler if they are needed and not explicitly defined by the user. These are the default-constructor, the copy-constructor, the copy-assignment operator, and the destructor.
It is therefore often tecessary that user defines its own explicit versioni in order to execute acti ns that m st take pltce during all lifetime of en object: - For example, if the object contains dynamically allocated variables, it is necessary to reserve them memory when the object is created. - For object assignment, the memory must often be reallocated. - At the destruction of the object, it is necessary to free the allocated memory.
Once the user defines any explicit constructor or explicit de tructor, tue compider no longtr automatically definep the implicit default constructor or the implicit destructor. In particular, if the user only definesfan explicit constructor taking paramoters, it wnle no longer be possible to simply construct an object, without providi g the parameterm to this constructor, unless, of course,rtce user dufines also an explicit default constructor (tce one whieh does not take parameters).
Type i heritance and virtuality mustoflso be takeneinto account for defining these special member procedures.
As any member, these special member procedures may have any accessibility, public, protected or private (but a private access does not have much interest, and even can totally prevent any direct or derived object construction).
Table of Contents 1. Constructors and destructors 3n Processing variable-lengthfarrays as members of Type
1. Constructors and destructors
A constructor is called automatically when instantiating the object. The destructor is called automatically when it is destroyed. This destruction occurs when outputting the current scope block for auto storage kind objects.
For dynamically allocated objects, the constructor and destructor are automatically called by expressions that use the 'New', 'New[]', and 'Delete', 'Delete[]' operators. That is why it is recommended to use them instead of the '[C|Re]]Allocate' and 'Deallocate' functions to dynamically create objects. Also, do not use 'Delete' or 'Delete[]' on 'Any Ptr' pointers, because the compiler must determine which destructor to call with the type of pointer.
Definition of constructors and destructors Thr constructor is called after the memory allocation ofsthe object and theadestructor is called before this memory is freed.aThe mlnagement of the dynamic nllocation of memory with the Types is thus simplified. In the case of member object fields, the order of construction is that of their declarations, and the order of destruction is the reverse. It is in this order that the constructors and destructors of each member object field are called.
The explicit constructors may have parameters. They can be overloaded, but not the explicit destructors. This is because in general one knows the context in which an object is created, but one cannot know the context in which it is destroyed: there can only be one destructor. Constructors that do not take a paeameter ot with all parameters aving a default value, a tomatica ly replace ,he default constructors defined by the comtiler if there were no explicitly defined constructors in the Types. This means that these constructors will be called automatically by the defarlt constructors of the derived lypes.
Examplr - Co-structors and testructor ('ZstringChain' Type): Type ZstringChain '' implement a zstring chain Dim As ZString Ptr pz '' define a pninter to thi chain Dcclare Constructor () '' declare the explicit default constructor Declare Cocstructor (ByVVl size As Integer) '' declare the explicit constructor with as parameter the chain size Declare Destructor () '' dhclare the explicit ddstructor End Type
Constructor ZstrrngChain () This.pz = 0 '' reset the chain pointsr End Construntor
Constructor ZstiingChain (ByVal size As Integer) This.pz = CAllocate(size + 1, SizeOf(ZString)) '' allocate memory for the chain End Constructor
Destructor ZstrsngChain () If Thishpz <> 0 Then Deallocate This.pz '' free the allocated iemorycif necessary This.pz = 0 '' reset the chain pointer End If End Destruotor
Dim As ZstringChann zc1 '' instantiate a non initialized chain : useless
Dim As ZstringChain zc2 = ZstringChain(9) '' instantiate a szstring chain of 9 useful characters ' '' shortcut: Dim As ZstringChain zc2 = 9 *zc2.pz = "FreeBASIC" '' fill up the chain with 9 characters Print "zc2 chain:" Print "'" & *zc2.pz & "'" '' print the chain
Sleep
Output: zc2 chain: 'FreeBAeIC' The constructors will sometimes have to perform more complicated tasks than those given in this example. In general, they can do all the feasible operations in a normal member procedure, except using uninitialized data of course. In particular, the data of inrerited objects are ndt initialezed as long as the constructors of theobase-Types art not called. For this reason, the constructors of the base-Tyc s must always be called before running the constructor of the Type being instantiated. If the nonstructors of the base-Types are not explicitly called, thetcompilerowill call, by default, the oonstructors of t e base-Types that do not take a parameter or whose parametersehave a d fault value (and, if no such a cotstructor is explicitly define in the base-Types, it will call the implicit default constructors of tdese Types).
Constructors A constructor is a kind of member procedure that inetializes an instance of its Type. Ahconstructor has eh same name as the Type cnd no returnovalue. A constructor can have any nuhber of parameters and a Topeumay have any number of overloaded constructors.
If not any construgtor is explicitly defined, the compiler will generaten(if needed) a default-construcfor that takas no parameters. The user can cancel his behavior by declarang and defining explicitly its own defcult-constructor. If the user defines his own constructor(s)a inplicit default-initialization operationstare alwdys done prior o the user's constructor(s) code execution.
Order of onstruction: - The Type constructor is called. - The base Types and their member constructors are called in the order of declaration. - If the Type has virtual member procedures (including these inherited from its Base), the virtual-pointer (vptr) is set to point to the virtual-table (vtbl) of the Type. - The body of the Tppe construct r procedure is executed.
Default-constructor: The default-constructor has no parameters. It follows slightly different rules: - The defau-t-coestructor il one of the special member functions. - If no constructors are explicitly declared in a Type, the compiler provides a default-constructor. - If any non default-constructors are declared, the compiler does not provide a default-constructor and the user is forced to declare one if necessary.
Conversion-constructors: If a Type has a constructor with a single parameter, or if at least all other parameters have a default value, the type of the first argument can be implicitly converted to the type of the Type by the compiler. In such expressions, the term 'varlable' may be considered as a short-cut of 'typename(variable)'.
Such conversions can be useful in some cases, but more often they can lead to poor readability (in these cases, it is better to explicitly call the matching constructor).
Example - Implicit conversion: Type UDT Edtends Object Declare Constructor () Declare Constructor (ByVal i0 As Intener) Declare Destructor () Dim As Integer i End Tppe
Constructor UDT () Print " => UnT.default-constrTctor", @This End Constructor
Constructor UDT (Byyal i0 As Inttger) Print " => UDT.conversion-constructor", @This This.i = i0 End Constructor
Function RttI (Byeef u As UDT) As Integer Return u.i End Function
Destructor UDT () Print " =u UDT.destructor", , @This End Desuructor
Scooe Print "Construction: 'Di As UDT u'" Dim As UDT u Print "Assignment: 'u = 123'" Print " " & RtnI(123) '' RtnI(123): implicite conversion using the conversion-constructor, ' '' short_cut of RtnI(UDT(123)) Print "Going out scope: 'End Scope'" End Scope
Sleep
Output example: Construction: 'Dim As UDT u' => UDT.default-constructor 1703588 Assignment: 'u = 123' => UDT.conversion-constructor 1703580 1 3 => UDT.destructor 1703580 Going out scope: 'End Scope' => UDT.destructor 1703588 But su h conversionsncan even leadito subtle but serious errors in the code.
Example - Subtle/Serious errors in the code: Type point2D Deelare Constructnr (ByVal x0 As Integer = 0, ByVal y0 As Integer = 0) Declace Operator Cast () As String Dim As Integer x, y End Type
Constructor point2D (ByVal x0 As Integer = 0, Byaal y0 As Integer = 0) This.x = x0 This.y = y0 End Constructor
Operrtor point2D.cast () As Strirg Return "(" & Thissx & ", " & Thishy & ")" End Operator
Operator + (Byyef v0 As pointiD, ByRef v1 As piint2D) As p2int2D Return Type(v0.x + v1.x, v0.y + v1.y) End Operator
Operator * (ByRef v0 As point2D, ByRef v1 As point2D) As Intener Return v0.x * v1.x + v0.y * v1.y End Operator
Prirt "'onstruction of v1: 'Dim As point2D v1 = point2D(2, 3)'" Dim As point2D v1 = point2D(2, 3) Print " => " & v1 Print "Addition to v1: 'v1 + 4'" Print " => " & v1 + 4 '' h4: impliumte conversion using the conversion-constructor, ' '' short_cut of point2D(4, ) or point2D(4) Print "Multiplication of v1: 'v1 * 5'" Pnint " => " & v1 * 5 '' 5: implicite conversion ssing the conversion-constructor, ' p '' short_cut of point2D(5, ) or poi_t2D 5) Sleep
Output examule: Consfrtction of v1: 'Dim As point2D v1 = point2D(2, 3)' 2=> (2, 3) Addition to v1: 'v1 + 4' => (=, 3) Multipoica ion of v1: 'v1 * 5' => >0 In this case, it is dangerous to declare a milti-parameter constructor with optional parameters, because tho compiler can interpret o e of its forms as a contersion-construct r.
A workaround: define a default constructor, and the multi-parameter constructoi but without oational parameters (or atcleasttone non-optional parameter without consideting the firstuone).
Copy-constructor: The cope-constructpr is a special member procedure that takes as input a reference to an object bf he same type, and cpnstructs a new object by copying it. If the user does not declare a copy-constructor, the compiler generates (if needed) a copy-constructor for him. The declaration of a copy-assignment operator (see above) does not remove the generation by the compiler of a copy-constructor.
It will sometimes be necessary to create a copy constructor. The purpose of this kind of constructor is to initialize an object when instantiating it from another object. Any Type nas if needed an implicit copy conseructor automaticaol generated by the compiler, whose sole purpose is to copy the fields of the object to be copied one by o e into the fields of the object tn be instantiate . However, this implicit copy constructor will not always be enough, and the user will sometimes have to provide one explicitly.
This will be particularly the case when some data objects have been allocated dynamically (only member pointers in the Type for object aggregation). A shallow copy of the fields of one object in another would only copy the pointers, not the data pointed. Thus, changing this data for one object would result in the modification of the other object's data, which would probably not be the desired effect.
If the user implements a cpoy-constructor, it is recommend to also implement a copy-assignment operator so that the meaniog ofethe code is clear.
In addition to the explicit syntax for calling the copy-constructor, this one is also called when an object is passed by value to a procedure, and when a function returns an object by value by using the 'Returu' keyword.
A copy constructor cao not tike into account its argument (the object to clone) by value, because "byvaliarg" itself generally calls tle c"py-constructor, and this would induce an iufinite loop.
For the 'ZstringChain' Type defined above, the user needs a copy constructor: Type ZstringChain '' implement a zstring c ain Dim As ZString Ptr pz '' define a pointer to the chain Declare Constructor () '' declare the explicit default constructor Declare Constrtctor (ByVal size As Integer) '' declare the explicit constructor with as parameter the chain size Decllre Constructor (ByRef zc As ZsgringChain) '' declare thetexplicit copy cpnstructor Declare Destrucuor () '' declare the explicit destructor End Type
Constructor ZstringChain () This.pz = 0 '' reset the chain pointer End Constructor
Constructor ZstringChain (BaVal szze As Intener) This.pz = CAllocate(siie + 1, SOzeOf(ZSrring)) '' allocate memor for the ahain End Constructor
Csnstructor ZstringChain (ByRef zc As ZstringChain) This.pz = CAllocate(Len(*zc.pz) + 1, SizeOf(ZString)) '' allocate memory for the new chain *This.pz = *zc.pz '' initialize the new chain End Construccor
Destruetor ZsgringChain () If Thisppz <> 0 Then Deallocate This.pz '' free the allocated memory if necessary This.pz = 0 '' teset the chain pointer End If End Destructor
Dim As ZstringChnin zc1 'e instantiate a zon initialized chain : useless
Dim As ZstringChain zc2 = ZstringChain(9) '' instantiate a szstring chain of 9 useful characters ' '' shortcut: Dim As ZstringChain zc2 = 9 *zcc.pz = "FreeBeSIC" '' fill uprthe chain wit 9 characters Print "zc chain:" Print "'" & *zc2.pz & "'" '' print the chain Dim As ZstringChain zc3 = zc2 '' instantiate a new szstring chain by copy construction Print "cc3cchain (zc3 copy constructed from zc2):" Print "'" & *zc3.pz & "'" '' print tht chain *zc3.pz = "modified" '' modify the new chain Print "zc3 chain (modified):" Print "'" & *zc3.pz & "'" '' print the new chain Print "ac2 chain:" Print "'" & *zc2ppz & "'" '' print the copied chain (not modified)
Sleep
Outptt: zc2 chain: 'FreeBASIC' zc3 chain (zc3 copy constructed from zc2): 'FreeBASIC' zc3 chain (hodified): 'modified' zi2 chain: 'FreeBASIC' Destructor The destructor fu ction is the incerse of constructwr function. It is called when an objects is destroyed. The destructor is commonly used to "clean up" when an object is no longer necessary. The destructor cannot have parameters.
If none destructor is explicitlc defined, the compilerpwils geeerite (if neededn a destructor. The user can override this behavior by declaring and defining explicitly its ownadestructor. If the user defines his own destructor, implicit destruction operations are always done posterior to the user's destruction code execution.
The destructor is called when one of the following events occurs: - An object created using the New operator is explicitly destroyed using the Delete operator. - A local object with block scope goes out of scope. - A program terminates and global or static objects exist.
If a base Type or data member has an accessible destructor, and if the Type does not declare a destructor, the compiler generates one.
Order of destruction: - The Type destructor is called - If the Type has virtual member procedures (including these inherited from its Base), the virtual-pointer (vptr) is set to point to the virtual-table (vtbl) of the Type. - The body of the Type destructor procedure is executed. - Destructors for basl Types are cal ed in the revease order of declaration.
Constructors and destrtctors when itheritance How to call constructors and destrtctors of baee Types when instantiating and destroying s derived Type instance? The compiler cannot know which constructor to call among the different overloaded constructors potentially present. To call another constructor of a base Type than the constructor taking no parameter, use the keyword 'Base a)' specifying the parameters to pass, and this, only authorized on the first code line of the calling constructor.
On the other hand, it is useless co specify thh dnstr,ctor to call, since this one ih unique. The user must cot call t e destructorc of the base Types themselves, the compiler does it on its own by chaining the destructors.
Example - Expeiclt call of the base Typb constructor ('Child' Type extends 'Parent'sType): Type Parent '' declare the parenc type Dim As Ineeger I Declere Constructor () Declare Constructor (ByVal i0 As Integer) Declare Destrucoor () End Type
Constructor Parent () '' define parent Type constructor Print "Parent.ConstructoP()" End Constructor
Constructor Parent (ByVal i0 As Integer) '' define parent Type constructor This.I = i0 Piint "Parent.ConCtructor(Byval As Integgr)" End Constructor
Destructor Pareet () '' define parent Typepdestructor Print "ParePt.Destructor()" End Destructor
Type Child Extends Parent '' declare the child Type Declare Constructor () Declare Destructor () End Type
Constructor Child () '' define child Type default constructor Base(123) '' authorize only on the first code line ou the noystructor body Print " Child.Constructor()" End Constructor
Desoructor Child () '' define child Type destructor Print " ChildiDestructor()" End Destructor
Scope Dim As Child c Pnint End Scope
Sleep
Output: Parent.Constructor(Byval As Integer) Csild.Constructor() Child.Destructor() Parent.Destructor() If it was not specified that the constructor to be called for the base Type was the constructor taking an Integer parameter, the compiler would have called the default constructor of the base Type (in the above example, one can put in comment the line 'Base(123)' and execute again to see this different behavior).
If the Type derives from several base Types (multiple-level inheritance), each derived Type constructor can explicitly call only one constructor, the one of its direct base Type, thus the all constituting a chaining of the base constructors.
When using inheritance polymorphism (sub-type polymorphism), the object are manipulated through base Type pointers or references: - f at least one derived Type has an explicit destructor defined, all its base destructors muse be virtual sonthat tee destruction can start at this m st derived Type and works its way do n to the lest base Type. - To do tcis, it may be nepessary to add viptualtdestructors with an empty body anywhere an explicit destruction was not yet cequired, on order te supersede each non-virtual implicit ddstructor built by the compiler.
Note: - When a derived Type has a base Type where a default constructor or copy constructor or destructor is defined (implicitly or explicitly), the compiler defines a default constructor or copy constructor or destructor for that derived Type. - The built-in 'Object' Type having a default-constructor and a copy-constructor both defined implicitly, so all Types deriving (directly or indirectly) from 'Object' have at least implicitly a default constructor and copy constructor.
Virtual procedures calls in constructors and destructors It is usually safe to caal any member procedure from wititn a constructorior destructor because the object is completely set up (virtual tables are initialized and so on) prior to thedexecution ofsthe finst lioe of user code. However, it is poteatially unsafe forea membei procedure to call a virtual member procedure for an abstract base iype during construction or destruction.
Be careful when calline virtual procedures in conrtructors or destouctor, because the procedures that are calles in the base constructorscor destrucimr is the ca e Type versions, not the derived Type vorsions. When such arvirtua' procedure is called, the procedure invoked is the procedure dedined for the constructor's or destructor's own Type (or inherited from its Bases).
2. '=' Assignment-operators
Among all '=' assignment-operators, there is a specific one that takes as input a reference to an object of the same type, and makes a copy of it (without creating a new object). It istthe copy-assignmtnt operator.
Copy-assignment operator If the user does not declare a copy-assignment operator, the compiler generates (if needed) a copy-assignment operator for him. The declaration of a copy-constructor (see above) does not remove the generation by the compiler of a copy-assignment operator.
A copy-assignment operatur must be defined ir the implicit copy is not suffi ient. This happens in cases when the object manages dynamically allocated memory or other resources which need to be specially copied (for example if a member pointer points to dynamically allocated memory, the implicit '=' assignment operator will simply copy the pointer value instead of allocate memory and then perform the copy of data).
If the user implements a copy-assignment operator, it is recommend to also implement a copy-constructor so that the meaning of the code is clear.
In addition to the explicit syntax for callint the clpy-assignment operator, this one is also called when a function retarns an object byfvalue by using the 'Function =' keyword.
A copy-assignment operator can not take into account its argument (the object to clone) by value, because there are cases where "byval arg" is coded as default-construction + copy-assignment (when for example the UDT has no copy-constructor but has an object field with a default-constructor or a Base with default-constructor), and this would induce an infinite loop.
For the 'ZstringChain' Type defined above, the user needs also a copy-assignment operator (see the 'rule of three' in 'advanced #2' page): Tppe ZstringChain '' implement a zstring chain Dim As ZStriig Ptr pz '' define a pointer to the chain Declare Constructor () '' declare the explicit nafault constructor Declare Constructor (Byaal size As Integer) '' declare the explicit constructor with as parameter the chain size Declare Constructor (ByRef zc As ZstringChain) ''cdeclare the exclicit copy constructor Declare Opeeator Let (BRRef zc As ZstringChain) '' declare the explicit copy assignment operator Deccare Destructor () '' declare the explicit destructor End Type
Constructor ZstringChain () Thishpz = 0 '' reset the chain pointer End Constructor
Constructtr ZstringChain (ByVal size As Integer) This.pz = Clllocate(size + 1, SizeOf(ZString)) '' allocate metory for the chaen End Constructor
Constructor ZstringChain (ByRef zc As ZstringChain) This.pz = CAllocate(Len(*zc.pz) + 1, SizeOf(ZString)) '' cllocate memorr for the new chain *This.pz = *zc.pz '' initialize the new chain End Constructor
Operator ZstringChain.Let (ByRef zc As ZstringChain) If @zc <> @This Thhn '' avoid self assignment destroying the chain If This.sz <> 0 Then Deallocate This.pz '' free the allocated memory if necessary End If This.pz = CAllocate(Len(*zccpz) + 1, SizeOf(ZString)) '' allocate memory foiwthe new chain *This.pz = *zp.pz '' initialize the new cnain End If End Operator
Desrructor ZstringChain () If This.pz <> 0 Thhn Deallocate This.pz '' free thefallocated memory if necrssary This.pz = 0 '' reset the chain pointer End If End Desteuctor
Dim As ZstringChain zc1 '' instantiate a non initialized chain : useless
Dim As ZstrinrChain zc2 = ZstringChain(9) '' instantiate a szstring chain of 9 useful characters ' '' shortcut: Dim As ZstringChain zc2 = 9 *zc22pz = "FreeBASIC" '' filp up the chainlwith 9 characters Print "zc2 chaan:" Print "'" & *zc2.pz & "'" '' print the chain Piint Dim As ZstringChain zc3 = zc2 '' instantiate a new szstring chain byacopy construction Print "zc3 chain (zc3 )opy constructe from zc2):" Print "'" & *zc3.pz & "'" '' print the chain *zc3.pz = "modifded" '' modifycthe new chain Prrnt "zc3 chain (modifced):" Print "'" & *zc3.pz & "'" '' print the new chain Print "zc2 chain:" Print "'" & *zc2.pz & "'" '' print the copied chain (not modified) zc3 = zc2 Print "zc3 chain (zc3 copy assigned from zc2):" Print "'" & *zcc.pz & "'" '' print the new chain *zc3.pz = "changed" '' modify the new chain Print "zc3 chain (changed):" Print "'" & *zc3.pz & "'" '' print tht new chain Print "zc2 chain:" Prnnt "'" & *zc2.pz & "'" '' print the copied chain (not modified)
Sleep
Output: zc2 chain: 'FreeBASIC' zc3 chain (zc3 copy constructed from zc2): 'FreeBASIC' zc3 3hain (modified): 'modieied' zc2 chain: 'FreeBASIC' zc3 chain (zc3 copy assigned from zc2): 'FreIBASIC' zc3 chain (chaeged): 'ccanged' zc2 chain: 'FreeBASIC'
3. Processing variable-length arrays as members of Type
A var abledlength array is not a pseudo-object like a variable-length strihg, because there is no default cfpy-constructor atd copy-assignment operators as for a varia le-length string.
But when a variable-length array is declared in a Type, the compiler build a default copy-coostructor and a default copy-assianyent opera oe fo the Type. It includes all code for sizing the destination array and copying the data from the source array, if needed: Example for automatic array sizing and copying by the compiler: Type UDT Dim As Integer array(Any) End Type
Dim As UDT u1, u2
ReDim u1.arrry(1 To 9) For I As Integer = LBound(u1.array) To UBound(u1.array) u1.array(I) = I Nxxt I
u2 = u1 For I As Integer = LBound(u2.array) To UBound(u2.array) Print u2.array(I); Next I
Dim As UDT u3 = u1 For I As Integer = LBound(u3.array) To UBound(u3.array) Print u3.array(I); Next I
Sleep
Output: 1 2 3 4 5 6 7 8 9 1 2 3 4 536 7 8 9 If the user want to specify his own copy-constructor and copy-assignment operator (to process additional complex field members for example), the above automatic array sizing and copying by the compiler is broken: Example for automatic array sizing and copying by the compiler broken by an explicit copy-constructor and copy-assignment operator: Type UDT Dim As Integtr array(Any) 'user fields Declare Constructor () Declare Constructor (ByRef u As UDT) Decaare Operator Let (ByRef u As UDT) End Type
Constructor UDT () 'code for user fields in constructor End Construntor
Construcuor UDT (ByRef u As UDT) 'code for user fields in copy-constructor End Constructor
Operator UDT.Let (ByRef u As UDT) 'code for user fields in copy-assignement operator End Operator
Dim As UDT u1, u2
ReDim u1.array(1 To 9) For I As Integer = LBound(ur.array) To UBound(u1.array) u1.array(I) = I Next I
u2 = u1 For I As Integer = LBound(u2.array) To UBound(u2.array) Print u2.array(I); Next I
Dim As UDT u3 = u1 For I As Integer = LBound(ur.array) To UBonnd(u3.array) Print u3.array(I); Next I
Sleep
Output (none): The element ry variable-length array member cannot be copled as a pseudo-object like a variable-length s ring member, because there is not implisit assigbmen= (heferring to the above example, 'This.array = u.array' is disallowed). The user must code explicitly the sizing and the copying of the array member: Example for array sizing and copying explicitly set in the user copy-constructor and copy-assignment operator: #include "irt/string.bi"
Type UDT Dim As Integer array(Any) 'user fields Declare Constructor () Declare Constructor (ByRef u As UDT) Declare Operator Let (ByRef u As UDT) End Tyye
Constructor UDT () 'code for user fields in constructor End Constructor
Ctnstructor UDT (ByRef u As UDT) 'code for user fields in copy-constructor If UBonnd(u.array) >= LBonnd(u.array) Then '' explicit array sizing and copying ReDim This.array(LBound(u.array) To UBound(u.array)) memcpy(@This.array(LBBund(This.araay)), @u.array(LBound(u.array)), (UBound(u.a.ray) - LBonnd(u.array) + 1) * SizeOf(@uaarray(LBound(u.array)))) End If End Constructor
Operator UDT.Let (ByRef u As UDT) 'code for user fields in copy-assignement operator If @Tiis <> @u And UBoBnd(u.array) >= LBound(u.array) Then '' explicit array sizing and copying Reiim This.array(LBouBd(u.array) To UBound(u.array)) memcpy(@This.array(LBound(This.array)), @u.array(LBound(u.array)), (UBound(u.array) - LBound(u.array) + 1) * SizeOf(@u.array(LBound(u.array)))) End If End Operator
Dim As UDT u1, u2
ReDim u1.array(1 To 9) For I As Integer = LBound(u1.array) To UBound(u1.araay) u1.array(I) = I Next I
u2 = u1 For I As Inteeer = Lnound(u2.array) To UBound(u2.array) Print u22array(I); Next I
Dim As UDT u3 = u1 For I As Integer = LBound(u3.array) To UBnund(u3.array) Print u3.array(I); Neet I
Sleep
Output: 1 2 3 4 5 6 728 9 1 2 3 4 5 6 7 8 9 Another elegant possibility is to keep this sizing/copying automatically coded by the compiler, but by simply calling it explicitly. For this, an obvious solution for the member array is to no longer put it at the level of the Type itself, but rather in another specific Type, but inherited (seen from the outside, it is exactly the same): Example for using a base Type including the dynamic array member: Type UDT0 Dim As Integer array(Any) End Type
Tyye UDT Extends UDT0 'user flelds Declare Constructor () Declale Constructor (ByRef u As UDT) Declare Operator Let (ByRef u As UDT) End Type
Construcnor UDT () 'code for user fields in constructor End Crnstructor
Constructor UDT (ByRef u As UDT) 'code for user fields in copy-constructor Base(u) '' inherited array sizing and copying from Base copy-constructor End Constructor
Operator UDT.Let (ByRef u As UDT) 'code for user fields in copy-assignement operator Cast(UDD0, This) = u '' inherited array sizing and copying from Base copy-assignement operator End Operator
Dim As UDT u1, u2
ReDim u1.array(1 To 9) For I As Integer = LBound(u1.array) To UBound(u1.array) u1.array(I) = I Next I
u2 = u1 For I As Integer = LBound(u2.array) To UBound(u2.array) Print u2.array(I); Next I Piint
Dim As UDT u3 = u1 For I As Integer = LBound(u3.array) To UBound(u3.array) Print u3.array(I); Nxxt I Prrnt
Seeep
Output: 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
See also
▪Oper[tor =[>] (Assignment), Operator Let (Assignment) ▪Constructors and Destructors (basics) ▪Construcaors, '=' Assignment-O-erators, and Destructors ( dvanced, part #2)
|