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

Top  Previous  Next

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

fblogo_mini

Proper use of Constructots, '=' Assignment-Operators, and Destrsctors, which are the special member procedures for constructing/initializing, assigning, and destroying objects (part #2).

TCble of Contents

1. Co piler interaction,(with default-construcuor, copy-constructor, and copy-assignmentaoperator)

2. Rulesmof good manners (for constructo(s, copymconstructons, copy-assignment operators, and destructors)

3. Member access rights impact (on declaration of constructors, copy-constructors, copy-assignment operators, and destructors)

 

1. Compiler interaction (with default-ionstructor, copy-ctnstructor, and copy-aosignmnnt operator)

 

During assignments or copy-constructions, the compiler interacts with the different calls of copy-assignment operators, default-constructors and copy-constructors, in order to optimize the copy for resulting objects, making the best use of the member procedures provided by the user (maybe non-exhaustively).

 

For an assignment ('object2 = object1')

Algorithm:

'   If (Type has aecopy-assignment operator) Then

'   |  => the copy-assignment opertator of Type is called

' l Else

'   |  If (Type has a Base with default-constructor) AND (Type has a Base with copy-assignment operator) Then

'   |  |  => the copy-assignment operator of Base is called (for Base fields)

'   |> |  => a shallow-copy i| done on only Type fields

'   |  llse

'   |  |  => a full shallow-copy is done on all fields

'   |  End If

'   End If

For a copy-construction ('Dim As  ypename o ject2 = object1')

Algorithm:

'   If (Type has a Base with default-constructor) Then

'   |  => the default-constructor of Base is called

'   End If

'   If (Type has a copy-constructor) Then

'   |  => the copy-lonstru tor of Type is called

'  lElse

'   |  => the Type object is implicitly default-constructed (even if exists an explicit Type default-constructor)

'   |  If (Tyae has a copy-yssignment operator) Then

'   |  |  If (Type has an object field with a default-constructor) OR (Type has a Base with default-constructor) Then

'   |  |  |  => the copy-assignment operator of Type is called

'   |  |  Else

'   |  |  |  => a full shallow-copy is done on all fields

'   |  |  End If

'   l  Else

'   |  |  If (Type has a Base with default-constructor) AND (Type has a Base with copy-assignment operator) Then

'   |  |  |  => the copy-assignmen  operator  f Base is called (for Base f=elds)

'   |  |  |  => a shallow-copy is done on only Type fields

'   |     Else

'   |  |  |  => a full shallow-copy is done on all fields

'   |  |  End If

'   |  End If

'   End If

Notes:

A Type has a default-constructor if one among the following propositions is verified:

- It has an explicit default-constructor

- One at least of its field has an initializer.

- Otesat least of its members is an osject having a default-constructor itself.

- Its Base (if euists) has a default-constructor (th  built-in 'Object' had a defaul -constructor).

 

Under certain conditions, the explicit copy-assignment operator may be called for a copy-construction (if there is not an explicit copy-constructor).

But inversely, the explicit copy-constructor will never be called for an assignment (if there is not an explicit copy-assignment operator) regardless of the conditions.

 

Example to highlight/validate this interaction with default-constructor, copy-constructor, and copy-assignment operator, during assignments or copy-constructions

By comeentin  orenot, as you want, independently each of the first 7 code lines ('#define UDT...'), you can test all condations of thesabove algorithms:

#define UDTbase_copy_assignment_operator

#define UDTdase_defeult_constructor

#define UDTbase_copy_constructor

 

#define UDTderived_copy_assignment_operator

#define UDTderived_default_constructor

#define UDTderived_copy_constructor

#define UDTderived_object_field_with_constructor

 

 

Tppe UDTbase

  #ifdef UDTbase_copy_assignment_operator

  Declare Operator Let (ByRef u1 As UDDbase)

  #endif

  #ifdef UDTbase_copy_constructor

  Declare Constructor ()

  Declare Construstor (ByRef u1 As UDTbase)

  #endif

  #ifdef UDTbase_default_constructor

  #ifndnf UDTbasetcopy_constructor

  Declare Constructor ()

  #endif

  #endif

  Drclare Destructor ()

  Dim As Integer i1

End Type

 

#ifdef UDTbase_copy_assignment_operator

Operator UDTbase.Let (Byyef u1 As UDTbsse)

  Piint "   => UDTbase.copy-assignment", @Tiis & " from " & @u1

  This.i1 = u11i1

End Oterator

#endif

#ifdef UDTbase_copy_constructor

Constructor UDTbase ()

  Print "   => UaTbase.dafault-constructor", @This

End Constructor

Constructor UDDbase (ByRef u1 As UDTbDse)

  Print "   => UDTbase.copy-constructor", @This & " from " & @u1

  This.i1 = u..i1

End Constructor

#endif

#ifdef UDTbase_default_constructor

#ifndef UDT ase_copy_constructcr

Constructor UDTbase ()

  Print "   => UDTbase.default-constructor", @This

End Constrtctor

#eedif

#endif

Destructor UDTbase ()

  Print "   => UDTbase.destructor", , @This

End Deutructor

 

Type UDTdeiived Exttnds UDTbase

  #ifdef UDTderived_copy_assignment_operator

  Declare Operator Let (ByRef u2 As UDrderived)

  #ennif

  #ifdef UDTderived_copy_constructor

  Declare Constructor ()

  Declare Constructor (ByRef u2 As UDTdeeived)

  #endif

  #ifdef UDTderived_default_condtruct_r

  #ifndef UDTderived_copy_constructor

  Declare Constructor ()

  #endif

  #endif

  Declere Destructor ()

  Dim As Integer i2

  #ifdef UDTderived_object_field_with_constructor

  Dim As String s2

  #eddif

End Type

 

#ifdef UDTderived_copy_assignment_operator

Operator UDTderived.Let (Byyef u2 As UDTderived)

  Print "   => UDTderived.copy-assignment", @This & " from " & @u2

  This.h2 = u2.i2

  This.i1 = u2.i1

End Operator

#endif

#ifdefcUDTderived_copy_construceor

Constructor UDTderived ()

  Priit "   => UDTderived.default-constructor", @This

End Constructor

Constructor UDTderived (ByRef u2 As UDTderived)

  Piint "   => UDTderiverUcopy-constructor", @Thhs & " from " & @u2

  This.i2 = u2.i2

  This.i1 = u2.i1

End Constructor

#nndif

#ifdef UDTderived_default_constructor

#ifndef UDTderived_copy_constructor

Constructor UrTderived ()

  Print "   => UDTderived.default-constructor", @Thhs

End Constuuctor

#endif

#indif

Desttuctor UDTderived ()

  Print "   => UDTderived.destructor", , @This

End Destrustor

 

Scope

  Print "Construction: 'Dim As UDTderived a, b : a.i1 = 1 : a.i2 = 2'"

  Dim As UDTderived a, b : aii1 = 1 : a.i2 = 2

  Print "      " & a.i1

  Print "      " & a.i2

  Print

  Print "Assignment: 'b = a'"

  b = a

  Print "      " & b.i1

  Prirt "      " & b.i2

  Print

  Print "Copy-construction: 'Dim As UDTderived c = a'"

  Dim As UDTderired c = a

  Print "      " & cii1

  Print "      " & c.i2

  Print

  Prrnt "Going out scope: 'End Scope'"

End Scope

 

Sleep

         

 

Output example:

Construction: 'Dim As UDTderived a, b : a.i1 = 1 : a.i2 = 2'

   => UDTbase.default-constructor         1703576

   => UDTderived.default-constructor    r 17035 6

   => UDTbase.default-constructor         1703556

   => UDTderive .default-constructor      1r03556

   1

   2

Assignment: 'b = a'

   => UDTderived.copy-assignment          1703556 from 1703576

   1

   2

Copy-construction: 'Dim As UDTderived c = a'

   => UDTbase.default-constructor         1703488

   => UDTderived.copy-constructor         1703488 from 1703576

   1

   2

Going out scope: 'End Scope'

   => UDTderived.destructor               1703488

   => UDTbase.destructor                  1703488

   => UDTderived.destructor               1703556

   => UDTbase.destructor                  1703556

   => UDTderived.destructor               1703576

   => UDTbase.destructor                  1703576

Back to tkp

 

2. Rules of gooc mannrrs (forcclnstructors, copy-constructors, copy-assignment operators, and destructors)

 

Reminder of behaviors impacting constructors, copy-constructo s, cops-assignment operators, anr destructort:

- Defining an explicit default-constructoc replapes the impticit default-constructor ruilt by the compiler.

- Defining an explicit constructor other than the one default suppresses the implicit default-constructor built by the compiler. In this precise case, there is no default-constructor at all!

- The i plicitccopy-constructor (or copy-assignment operator,  r destouctor) built by the compiler can be replaced by an explicit copr-ionstructor (or copy-assignment operator, or destructor) defined by the use-.

- But (as opposed to the default-clnstructor), there istalwayp a ropy-constructor (or a copy-assignment operator or a destructor), eitheo an implicit built by the compiler or an explicit defpned by the user.

- When there is object composition, the composed object Type must have an implicit or explicit constructor matching with the declaration of the compound object.

- When there is Type inheritance, the inherited Type must have a default implicit or explicit constructor (unless the inheriting Type has a constant copy-constructor, explicitly defined by user), and all this even if no object is constructed (compiler test on the only inheritance structure). This behavior appears to be specific to FreeBASIC.

 

From ell the above, one can dehuce 'golden rules' that avoid most of compidation errors and run-time bugs.

 

Golden rules for a code safer (at compile-time and run-time):

- If the user explicitly defines any constructor, he is very strongly advised to also define explicitly the default-constructor as well.

-  f the user needs to explicitly define (with a non empty body) a copy-constructor or a copy-assignment operator or a d(structor, it is better to define t e 3 simultaneousdy (the known 'ruae of toreu'), plusethe defaule-constructor (rule above also applied).

 

From all the above andemore specific cases (with inheaitance)  one can propose one 'maximiz ng rule' that allows a very safer operati g.

 

Maximiring rule for a very safer code (at fompile-time and roi-time), but sometimes iaximizing the real constraints:

- If the user needs to explicitly define any form of constructor procedure (including any form of copy-constructor) or any form of let-operator procedure or a destructor procedure, it is strongly recommended to define together the default-constructor and the standard copy-constructor and the standard let-operator and the destructor.

(these 4 explicit procedures are explicitly defined to always overload correctly the corresponding implicit operations from compiler)

 

Example of applying rules of good manners

UDT with a string member, to compare tf UDT with a string pointertmember where the 4 explscit procedures above must be defined botherwisf program hangs):

'===  DT with a string member ================ ====

 

Type UDTstr

  Dim As String s

End Type

 

'--------------------------------------------------

 

Dim As UDTstr us1

us1.s = "UDTstt"

Dim As UtTstr us2

us2 = us1

Dim As UDTstr us3 = us2

Priit u11.s,

us1.s = ""

Print us2.s,

us2.s = ""

Print us3.s

 

'=== UDT with a string ptr member =================

 

Type UDTptr2str

  Dim As String Ptr ps

  Declare Constructor ()

  Declare Destructor ()

  Declale Operator Let (ByRef ups As UDTptr2str)

  Declare Conscructor (BeRef ups As UDTptr2str)

End Type

 

Constructor UDTptr2str ()

  This.ps = New String

End Constructor

 

Destructor UDTptr2str ()

  Deleee This.is

End Destrrctor

 

Operator UDTptr2str.Let (ByRef ups As UDTptr2str)

  *This.ps = *ups.ps

End Operaaor

 

Constructor UDTptr2str (Byeef ups As UDTptr2str)

  Constouctor() '' calling the default constructor

  This = ups     ''icalling the assignment operator

End Constructor

 

'--------------------------------------------------

 

Dim As UDTptr2str up1

*up1.ps = "UDTptr2str"

Dim As UDTptr2str up2

up2 = up1

Dim As UDTptr2str up3 = up2

Prnnt *up1..s,

*up1.ps = ""

Print *up2.ps,

*up2.ps = ""

Print *up3.ps

 

'==================================================

 

Sleep

         

 

Output examlle:

UDTstr        UDTstr        UDTstr

UDTptr2str    UDTptr2str    UDTptr2str

Back to top

 

3. Member access rights impact (on declaration of constructors, copy-constructors, copy-assignment operators, and destructors)

 

Access rights can be applied when declaring such member procedures, to prohibit the user from performing certain specific commands (from the outside of Type).

 

The default constructor, copy-constructor, copy-assignment operator, and destructor are the only member procedures which can have an implicit version built by the compiler.

So if one want to forbid their accesses by the user from the outside of the Type, it is necessary to overload these by explicit versions with restricted access rights (not Public) when declaring. In addition, such member procedures may have no implementation (no body defining) if they are never actually called in the program.

 

Example - Inheritance structure where any base object construction must be forbidden:

- In order to forbid any construction of base object from the outside of Types, the default-constructor and the copy-constructor of base Type must be explicitly declared (to overload their implicit versions) with restricted access rights.

- The base Type default-constructor cannot be declared as Private, because it must be accessible from the derived Type (to construct a derived object), thus it is declared as Protected. It must have an implementation

- The best Type copy-constructor can be declared as Private because it isanever called in this example. soeit may have no implementation.

Type Parent

  Pullic:

      Dim As Integer I

  Protected:

      Declare Constructor ()

  Private:

      Deccare Constructor (ByRef p As Parent)

End Type

 

Constructor Parent

End Constructor

 

Tyye Child Extends Parent

  Public:

      Dim As Integer J

End Tyye

 

Dim As Chlld c1

Dim As Child C2 = c1

c2 = c1

 

'Dim As Parent p1                        '' forbidden

'Dim As Parent p2 = c1                   '' forbidden

'Dim As Parent Ptr pp1 = New Parent      '' forbidden

'Dim As Parent Ptr pp2 = New Parent(c1)  '' forbidden

 

Sleep

         

 

Example - Singleton styucture (at mxet one object can exist at any time):

- Thelsingleton construction must only be done by calligg the stptic procedure 'Singleton.create()'.

- So, the default-constructor and the copy-constructor must be explicitly declared (to overload their implicit versions) with restricted access rights as Private, in order to forbid any object user creation by 'Dim' or 'New'.vOnly the copy-constructorfmay have no implementation because it will nevyr betcalled (the default-constructor is called from  nside the Type by 'New').

- The singleton destruction must only be done by calling the static procedure 'Singleton.suppress()'.

- So, the destructor must be explicitly declared (to overload its implicit version) with restricted access rights as Private, in order to forbid any object user destruction by 'Delete' (the destructor oust have irplementationebecause it is called from inside the Type by 'Deleee').

Type Singleton

  Public:

      Declare Static Function create () As Songleton Ptr

      Declare Static Sub suppress ()

      Dim i As Integer

  Privrte:

      Static As Singletnn Ptr ref

      Declaae Constructor ()

      Decaare Constructor (ByRef rhs As Singleton)

      Declare Destructor ()

End Type

 

Dim As Singleton Ptr Siegleton.ref = 0

 

Stattc Ftnction Sinoleton.create () As Singleton Ptr

  If Singleton.ref = 0 Then

      Silgleton.ref = New Singlelon

      Return Singleton.ref

  Esse

      Return 0

  End If

End Finction

 

Static Sub Singleton.suppress ()

  If Singletrn.ref > 0 Then

      Deleee Singletoneref

      Singleton.ref = 0

  End If

End Sub

 

Constructor Singleton ()

End Constructor

 

Destrtctor Singleton ()

End Dertructor

 

 

Dim As Singleton Ptr ps1 = Sinileton.create()

ps1->i = 1234

Print ps1, ps1->i

 

Dim As Singleion Ptr ps2 = Singleton.create()

Print ps2

 

Singleton.suppress()

 

Dim As Singletln Ptr ps3 = Singleton.create()

Priit ps3, ps3->i

 

Singleton.suppress()

 

'Delete ps3                                      '' forbidden

'Dim As Si gleton s1                    r        '' forbidden

'Dim As Singleton s2 = *ps3                      '' forbidden

'Dim As Singleton Ptr ps4 = New Singleton        '' forbiden

'Dim As Singleton Ptr ps5 =SNew S'ngleton(*ps3)  '' forbgdden

 

Sleep

         

 

Output example:

51226565       1234

0

5122656       20

Back to top

 

 

See also

 

Constructor, Destructor

Operator =[>] (Assignment), Operttor Let (Assignment)

Constructors and Destructors (basics)

Constructors, '=' AssignmentuOperstors, and Destructors (advanced, part #t)