Constructors and Destructors
In charge of the creation and destruction of objects.
Overview
Declaration
Default constructors
Copy constructors
Calling constructors
Overview
Constructors and destructors are responsible for creating and destroying objects, respectively. In general, constructors give objects their initial state, that is, they give meaningful values to their objects' member data. Destructors perform the opposite function; they make sure any resources owned by their objects are properly freed.
Simply, constructors are special member procedures that are called when an object is created, and destructors are special member procedures called when an object is destroyed. Both constructors and destructors are called automatically by the compiler whenever an object is created or destroyed, whether explicitly with the use of the `Dim` or `New` keywords, or implicitly by passing an object to a procedure by value or through an object going out of scope.
Declaration
Constructors and destructors are declared like member procedures but with the `Constructor` keyword instead of `Sub` or `Function`, and without a name. Similarly, they are defined with only the name of the `Type` or `Class` they are declared in.
A `Type` or `Class` can have multiple constructors, but only one destructor.
Default constructors
Default constructors are constructors that either have no parameters, or all of their parameters have a default value. They are called when an object is defined but not initialized, or is created as part of an array, with the `Dim`, `ReDim` or `New`[] keywords. The first constructor declared in the example below is a default constructor.
Copy constructors
Copy constructors are constructors called when an object is created, or cloned, from another object of the same type (or an object that can be converted to that type). This happens explicitly when initializing an object with another object, or implicitly by passing an object to a procedure by value. Copy constructors are declared having one parameter: an object of the same type passed by reference.
Copy constructors are only called when creating and initializing object instances. Assignment to objects is handled by the `Member Operator Let`.
Calling constructors
Unlike other member procedures, constructors are generally not called directly from an object instance. Instead, a constructor is specified in a `Dim` statement either with an initializer or without one, or in a `New` statement with or without arguments.
When specifying an initializer for an object, the name of the type followed by any arguments it requires is used.
`
Type foo
'' Declare a default ctor, copy ctor and normal ctor
Declare Constructor
Declare Constructor (ByRef As foo)
Declare Constructor (As Integer)
'' Declare a destructor
Declare Destructor
ints As Integer Ptr
numints As Integer
End Type
'' Define a constructor that creates 100 integers
Constructor foo
ints = New Integer(100)
numints = 100
End Constructor
'' Define a constructor that copies the integers from another object
Constructor foo (ByRef x As foo)
ints = New Integer(x.numints)
numints = x.numints
End Constructor
'' Define a constructor that creates some integers based on a parameter
Constructor foo (n As Integer)
ints = New Integer(n)
numints = n
End Constructor
'' Define a destructor that destroys those integers
Destructor foo
Delete[] ints
End Destructor
Scope
'' calls foo's default ctor
Dim a As foo
Dim x As foo Ptr = New foo
'' calls foo's copy ctor
Dim b As foo = a
Dim y As foo Ptr = New foo(*x)
'' calls foo's normal ctor
Dim c As foo = foo(20)
Dim z As foo Ptr = New foo(20)
'' calls foo's dtor
Delete x
Delete y
Delete z
End Scope '' <- a, b and c are destroyed here as well
`
Compiler-provided constructors and destructors
If no copy constructor is declared for a `Type` or `Class`, the compiler provides one. If no constructor has been declared, the compiler also provides a default constructor.
The compiler-provided default constructor initializes member data to default values, that is, numeric and pointer members are set to zero (0), and object members are default-constructed. The copy constructor that the compiler declares shallow-copies all member data from one type to another: numeric and pointer types are initialized with the corresponding data members in the object that is copied, and object members are copy-constructed from their corresponding object members. This means that dynamic resources, such as memory pointed to by a pointer data member, is not copied; only the address is copied. So if an object owns a resource, meaning it is responsible for its creation and destruction, then the compiler-generated copy constructor will not be sufficient.
If a destructor is not declared, the compiler generates one. This destructor calls object members' destructors and does nothing for numeric and pointer types. Again, if an object owns a dynamic resource, then the compiler-generated destructor will not be sufficient, as the resource will not be freed when the object is destroyed.
This is commonly referred to as the "Rule of 3": If an object needs a custom copy constructor, assignment operator or destructor, chances are it needs all three.