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.
This article takes into account Type inheritance and virtuality.
The default-constructor and the copy-constructor are specific constructors because of their setting of parameters.
The copy-assignment operator is a specific assignment operator because of its setting of parameters.
As any member, these 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).
Syntax for their declarations and definitions:
- - Constructors:
- Declaration within Type:
Definition outside Type:Declare Constructor ( [ ... [, ... ] ] )Constructor typename ( [ ... [, ... ] ] )
.....
End Constructor
- Declaration within Type:
Definition outside Type:Declare Operator Let ( ... )Operator typename.Let ( ... )
.....
End Operator
- Declaration within Type:
Definition outside Type:Declare Destructor ( )Destructor typename ( )
.....
End Destructor
- Declaration within Type:
- A constructor is a kind of member procedure that initializes an instance of its Type. A constructor has the same name as the Type and no return value. A constructor can have any number of parameters and a Type may have any number of overloaded constructors.
If not any constructor is explicitly defined, the compiler will generate (if needed) a default-constructor that takes no parameters. The user can cancel this behavior by declaring and defining explicitly its own default-constructor.
If the user defines his own constructor(s), implicit default-initialization operations are always done prior to the user's constructor(s) code execution.
Order of Construction:- 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 (vtable) of the Type.
- The body of the Type constructor procedure is executed.
- 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 'variable' 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 of implicit conversion:Code: Select all
Type UDT Extends Object Declare Constructor () Declare Constructor (Byval i0 As Integer) Declare Destructor () Dim As Integer i End Type Constructor UDT () Print " => UDT.default-constructor", @This End Constructor Constructor UDT (Byval i0 As Integer) Print " => UDT.conversion-constructor", @This This.i = i0 End Constructor Function RtnI (Byref u As UDT) As Integer Return u.i End Function Destructor UDT () Print " => UDT.destructor", , @This End Destructor Scope Print "Construction: 'Dim 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
Code: Select all
Construction: 'Dim As UDT u' => UDT.default-constructor 1703588 Assignment: 'u = 123' => UDT.conversion-constructor 1703580 123 => UDT.destructor 1703580 Going out scope: 'End Scope' => UDT.destructor 1703588
Example of subtle/serious errors in the code:Code: Select all
Type point2D Declare Constructor (Byval x0 As Integer = 0, Byval y0 As Integer = 0) Declare Operator Cast () As String Dim As Integer x, y End Type Constructor point2D (Byval x0 As Integer = 0, Byval y0 As Integer = 0) This.x = x0 This.y = y0 End constructor Operator point2D.cast () As String Return "(" & This.x & ", " & This.y & ")" End Operator Operator + (Byref v0 As point2D, Byref v1 As point2D) As point2D Return Type(v0.x + v1.x, v0.y + v1.y) End Operator Operator * (Byref v0 As point2D, Byref v1 As point2D) As Integer Return v0.x * v1.x + v0.y * v1.y End Operator Print "Construction 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 '' 4: implicite conversion using the conversion-constructor, ' '' short_cut of point2D(4, ) or point2D(4) Print Print "Multiplication of v1: 'v1 * 5'" Print " => " & v1 * 5 '' 5: implicite conversion using the conversion-constructor, ' '' short_cut of point2D(5, ) or point2D(5) Sleep
Code: Select all
Construction of v1: 'Dim As point2D v1 = point2D(2, 3)' => (2, 3) Addition to v1: 'v1 + 4' => (6, 3) Multiplication of v1: 'v1 * 5' => 10
- In this case, it is dangerous to declare a multi-parameter constructor with optional parameters, because the compiler can interpret one of its forms as a conversion-constructor.
For other comment about risk due to implicit conversion, see the feature request: #291 Add optional specifier 'Explicit' for constructor declaration to inhibit use by compiler in implicit conversions
- The default-constructor has no parameters.
It follows slightly different rules:- The default-constructor is 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.
- The copy-constructor is a special member procedure that takes as input a reference to an object of the same type, and constructs 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 does not remove the generation by the compiler of a copy-constructor.
A copy-constructor must be defined if the implicit copy construction is not sufficient. This happens in cases when the object manages dynamically allocated memory or other resources which need to be specially constructed or copied (for example if a member pointer points to dynamically allocated memory, the implicit copy construction will simply do an implicit pointer construction and a value copying instead of allocate memory and then perform the copy of data).
If the user implements a copy-constructor, it is recommend to also implement a copy-assignment operator so that the meaning of the 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 'Return' keyword.
- The copy-assignment operator is a special member procedure that takes as input a reference to an object of the same type, and makes a copy of it (without creating a new object).
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 does not remove the generation by the compiler of a copy-assignment operator.
A copy-assignment operator must be defined if the implicit copy is not sufficient. 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 calling the copy-assignment operator, this one is also called when a function returns an object by value by using the 'Function =' keyword.
- The destructor function is the inverse of constructor 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 explicitly defined, the compiler will generate (if needed) a destructor. The user can override this behavior by declaring and defining explicitly its own destructor.
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.
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 (vtable) of the Type.
- The body of the Type destructor procedure is executed.
- Destructors for base Types are called in the reverse order of declaration.
- It is usually safe to call any member procedure from within a constructor or destructor because the object is completely set up (virtual tables are initialized and so on) prior to the execution of the first line of user code. However, it is potentially unsafe for a member procedure to call a virtual member procedure for an abstract base Type during construction or destruction.
Be careful when calling virtual procedures in constructors or destructor, because the procedures that are called in the base constructors or destructor is the base Type versions, not the derived Type versions. When such a virtual procedure is called, the procedure invoked is the procedure defined for the constructor's or destructor's own Type (or inherited from its Bases).
- 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'):For a copy-construction ('Dim As typename object2 = object1'):Code: Select all
' If (Type has a copy-assignment operator) Then ' | => the copy-assignment opertator of Type is called ' 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 is done on only Type fields ' | Else ' | | => a full shallow-copy is done on all fields ' | End If ' End If
Notes:Code: Select all
' 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-constructor of Type is called ' Else ' | => the Type object is implicitly default-constructed (even if exists an explicit Type default-constructor) ' | If (Type has a copy-assignment 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 ' | 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 is done on only Type fields ' | | Else ' | | | => a full shallow-copy is done on all fields ' | | End If ' | End If ' End If
- 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.
- One at least of its members is an object having a default-constructor itself.
- Its Base (if exists) has a default-constructor (the built-in OBJECT has a default-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.
- By commenting or not, as you want, independently each of the first 7 code lines ('#define UDT...'), you can test all conditions of the above algorithms:
Code: Select all
#define UDTbase_copy_assignment_operator #define UDTbase_default_constructor #define UDTbase_copy_constructor #define UDTderived_copy_assignment_operator #define UDTderived_default_constructor #define UDTderived_copy_constructor #define UDTderived_object_field_with_constructor Type UDTbase #ifdef UDTbase_copy_assignment_operator Declare Operator Let (Byref u1 As UDTbase) #endif #ifdef UDTbase_copy_constructor Declare Constructor () Declare Constructor (Byref u1 As UDTbase) #endif #ifdef UDTbase_default_constructor #ifndef UDTbase_copy_constructor Declare Constructor () #endif #endif Declare Destructor () Dim As Integer i1 End Type #ifdef UDTbase_copy_assignment_operator Operator UDTbase.Let (Byref u1 As UDTbase) Print " => UDTbase.copy-assignment", @This & " from " & @u1 This.i1 = u1.i1 End Operator #endif #ifdef UDTbase_copy_constructor Constructor UDTbase () Print " => UDTbase.default-constructor", @This End Constructor Constructor UDTbase (Byref u1 As UDTbase) Print " => UDTbase.copy-constructor", @This & " from " & @u1 This.i1 = u1.i1 End Constructor #endif #ifdef UDTbase_default_constructor #ifndef UDTbase_copy_constructor Constructor UDTbase () Print " => UDTbase.default-constructor", @This End Constructor #endif #endif Destructor UDTbase () Print " => UDTbase.destructor", , @This End Destructor Type UDTderived Extends UDTbase #ifdef UDTderived_copy_assignment_operator Declare Operator Let (Byref u2 As UDTderived) #endif #ifdef UDTderived_copy_constructor Declare Constructor () Declare Constructor (Byref u2 As UDTderived) #endif #ifdef UDTderived_default_constructor #ifndef UDTderived_copy_constructor Declare Constructor () #endif #endif Declare Destructor () Dim As Integer i2 #ifdef UDTderived_object_field_with_constructor Dim As String s2 #endif End Type #ifdef UDTderived_copy_assignment_operator Operator UDTderived.Let (Byref u2 As UDTderived) Print " => UDTderived.copy-assignment", @This & " from " & @u2 This.i2 = u2.i2 This.i1 = u2.i1 End Operator #endif #ifdef UDTderived_copy_constructor Constructor UDTderived () Print " => UDTderived.default-constructor", @This End Constructor Constructor UDTderived (Byref u2 As UDTderived) Print " => UDTderived.copy-constructor", @This & " from " & @u2 This.i2 = u2.i2 This.i1 = u2.i1 End Constructor #endif #ifdef UDTderived_default_constructor #ifndef UDTderived_copy_constructor Constructor UDTderived () Print " => UDTderived.default-constructor", @This End Constructor #endif #endif Destructor UDTderived () Print " => UDTderived.destructor", , @This End Destructor Scope Print "Construction: 'Dim As UDTderived a, b : a.i1 = 1 : a.i2 = 2'" Dim As UDTderived a, b : a.i1 = 1 : a.i2 = 2 Print " " & a.i1 Print " " & a.i2 Print Print "Assignment: 'b = a'" b = a Print " " & b.i1 Print " " & b.i2 Print Print "Copy-construction: 'Dim As UDTderived c = a'" Dim As UDTderived c = a Print " " & c.i1 Print " " & c.i2 Print Print "Going out scope: 'End Scope'" End Scope Sleep
Code: Select all
Construction: 'Dim As UDTderived a, b : a.i1 = 1 : a.i2 = 2' => UDTbase.default-constructor 1703576 => UDTderived.default-constructor 1703576 => UDTbase.default-constructor 1703556 => UDTderived.default-constructor 1703556 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
- A Type has a default-constructor if one among the following propositions is verified:
- How to right Use Dynamic (variable-length) Arrays in FB UDTs