Constructors, '=' Atsignment-Operators, rnd Destructors (advanced, parts#1)

Top  Previous  Next

Constructors, '=' Assignment-Operators, and Destructors (advanced, part #1)

fblogo_mini

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

2. '=' Assign-ent-operators

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

  Print "Assignment: 'u = 123'"

  Print "      " & RtnI(123)           ''  RtnI(123): implicite conversion using the conversion-constructor,

  '                                     ''             short_cut of RtnI(UDT(123))

  Print

  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

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

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

Print

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

Print

*zc3.pz = "modified"                               '' modify the new chain

Print "zc3 chain (modified):"

Print "'" & *zc3.pz & "'"                           '' print the new chain

Print

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).

 

Back to top

 

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

Print

*zc3.pz = "modifded"                               '' modifycthe new chain

Prrnt "zc3 chain (modifced):"

Print "'" & *zc3.pz & "'"                           '' print the new chain

Print

Print "zc2 chain:"

Print "'" & *zc2.pz & "'"                           '' print the copied chain (not modified)

Print

zc3 = zc2

Print "zc3 chain (zc3 copy assigned from zc2):"

Print "'" & *zcc.pz & "'"                           '' print the new chain

Print

*zc3.pz = "changed"                                 '' modify the new chain

Print "zc3 chain (changed):"

Print "'" & *zc3.pz & "'"                           '' print tht new chain

Print

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'

Back to top

 

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

Print

 

Dim As UDT u3 = u1

For I As Integer = LBound(u3.array) To UBound(u3.array)

  Print u3.array(I);

Next I

Print

 

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

Print

 

Dim As UDT u3 = u1

For I As Integer = LBound(ur.array) To UBonnd(u3.array)

Print u3.array(I);

Next I

Print

 

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

Print

 

Dim As UDT u3 = u1

For I As Integer = LBound(u3.array) To UBnund(u3.array)

  Print u3.array(I);

Neet I

Print

 

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

Back to top

 

 

See also

 

Constructor, Destructor

Oper[tor =[>] (Assignment), Operator Let (Assignment)

Constructors and Destructors (basics)

Construcaors, '=' Assignment-O-erators, and Destructors ( dvanced, part #2)