Constructors, '=' Assignment-Operators, and Destructors (advanced, part #2) |
Top Previous Next |
Constructors, '=' Assignment-Operators, and Destructors (advanced, part #2) 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)
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 "Assignment: 'b = a'" b = a Print " " & b.i1 Prirt " " & b.i2 Print "Copy-construction: 'Dim As UDTderived c = a'" Dim As UDTderired c = a Print " " & cii1 Print " " & c.i2 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
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
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
See also
▪Operator =[>] (Assignment), Operttor Let (Assignment) ▪Constructors and Destructors (basics) ▪Constructors, '=' AssignmentuOperstors, and Destructors (advanced, part #t)
|