Home > Language Reference > Functions
Objects
Writing an application with HB++ rests principally on the use of classes and the instantiation of objects. This section presents the object model implemented by HB++ as well as the functions used to create and manipulate objects.
Declaration
Each time you create a new class module, form, user control or table in your project, you declare a class. This creates a new data type, whose name is set by the Name property of the module added, and which can be used with the New statement to create an instance of this class at runtime.
The HB++ language implements the concept of derivation. Each class has a parent class specified by the value of its Extends property, from which it inherits all methods and data. Used properly, this technique means you can write compact code, clear and reusable. The class hierarchy build into HB++ is a good example of using this mechanism. In particular, you can refer to the class hierarchy of the Database and Display objects.
There are however two situations where you cannot choose the parent class of a class. Tables added to your project always define classes deriving from Recordset, whilst user controls always define classes deriving from UserControl. You can choose the parent class of a form as long as it derives directly or indirectly from Form.
Each class also has an Extendable, Instantiable and Cloneable property. Refer to the documentation of these properties for more information.
Object Variables
You can declare variables that will contain objects as you declare variables that will contain other data, by using the Dim statement. There is a major difference between the variables that contain objects and the others: they can only contain references to these objects and not the objects themselves.
When you set the value of a String variable to another, you obtain two independent strings. Modifications applied to the original string do not affect the copy. On the other hand, when you set the value of an object variable to another, there is still only one physical object in memory. It is simply accessible from two different variables.
To highlight this difference, modifying values is achieved with the Let keyword whilst modifying object references is achieved with the Set keyword. Confusing these two keywords causes a compile-time error.
Instantiation and destruction of objects
The preferred way to instantiate a class consists of applying the New operator, either when declaring the variable or either in an expression. The type of the object to be created is specified after the keyword, as in the following example:
Dim c1 As New Collection ' Declaration and instantiation Dim c2 As Collection ' Declaration only Set c2 = New Collection ' Instantiation
When a variable does not reference any object, which is the case with c2 before the Set statement is reached, it contains the special value Nothing. The Is operator can be used to determine if a variable references an object or not.
The New keyword behaving as an operator, it can also be used wherever an expression returning an object is expected, to create new instances "on-the-fly". The following example creates a Collection and then directly pass a reference to this new object to the AddressOf function:
Dim addr As Long addr = AddressOf(New Collection)
The inconvenience of the New operator is that the type of object created is fixed during compilation. Sometimes, it is useful to be able to choose the object type to create depending on the state of the program and the value of certain variables. This can be achieved with the If...Then...Else or Select Case statements, but HB++ offers a more elegant alternative and above all quicker to implement.
The compiler associates each class with a numeric value called the type identifier. The TypeID keyword returns this identifier whilst the CreateInstance function creates a new instance of a class from its type identifier. The following example illustrates this:
Dim k as Long, f as Form k = TypeId(frmMain) ' Obtains the type identifier from the frmMain class Set f = CreateInstance(k) ' Creates an instance of the frmMain class
The documentation of the CreateInstance function gives a more detailed example of using this instantiation mechanism, adapted for a problem frequently encountered when designing the user interface: opening a different form depending on the value of a variable.
The last technique for instantiating an object is using the Clone function. This function creates a new object of the same type as that passed as a parameter, and copies the member variables one by one. This is useful for saving the state of an object before modifying its member variables, as the example shows on the page relating to this function.
Finally, contrary to other programming languages, it is not necessary to destroy objects when they are no longer used. A special library routine called garbage collector, checks the memory at regular intervals and automatically detects objects that are no longer used and frees them up. You can trigger the garbage collector by invoking the MemCompact method.
Types and type conversion
The real type of an object referenced by a variable does not necessarily correspond to the type with which the variable was declared. Because of the inheritance mechanism, a class will always have all of the functionality of its parent classes. There is therefore no disadvantage if a variable declared with the Display type, for example, contains in reality a Bitmap or Form object. On the other hand, the reverse is not true. A variable declared with the Form type cannot contain references to an object whose type is one of the parent classes of Form.
The immediate application of this possibility is writing generic methods. For example, the function below sends a string of characters in a stream:
Private Sub SendSomething(ByRef s as Stream) s.Write "Hello World !" End Sub
You can call this function and pass it any type of stream, like StreamSocket or StreamFile : it will still function because it only uses the methods defined in the generic Stream class.
In certain cases, it can be useful to determine the exact type of the object referenced by a variable, in order to undertake a particular action. This is what the ClassID property is made for: it returns the actual type of an object, you can compare to type identifiers obtained through the TypeID keyword. We can modify the previous example so that the function empties the stream if its type is StreamMemory otherwise it does nothing:
Private Sub SendSomethingEx(ByRef s as Stream)
s.Write "Hello World !"
If s.ClassID=TypeID(StreamMemory) Then ' Check the real type of the object passed
StreamMemory(s).Clear ' Insert an explicit type conversion so that
End If ' compiler recognizes the Clear method
End SubBut there is still a problem with this code: the StreamMemory class has derivatives, namely StreamRecord and StreamSslItem. Calling this function with one of these objects won't achieve the expected result, as they won't pass our ClassID test, nevertheless they do inherits of all of the StreamMemory features. This can be solve with the Implements function, that determine whether an object inherits of a given class:
Private Sub SendSomethingEx(ByRef s as Stream)
s.Write "Hello World !"
If s.Implements(TypeID(StreamMemory)) Then
StreamMemory(s).Clear
End If
End SubThis kind of programming is not recommended however because it is contrary to the principles of object oriented programming. It should only be used in the rare cases where it is impossible to proceed in any other way. Noteably in this example, our extended function loses its generic character which can be the source of programming errors that are complex and difficult to find.
Finally, the AddressOf and ObjectAt functions respectively return the memory address of an object and the object located at a given memory address. These two functions are useful for storing object references in variables of type Long, as for example the ItemData property in a List or PopUp control. In this sense, the AddressOf function can be seen as a conversion from an object to a type Long whilst the ObjectAt function can be seen as a conversion from type Long to an object.
You can also refer to the Data Types chapter for more information on type conversion, and notably to find out the error controls carried out by HB++ when converting object references.
Reference
The functions described in this chapter are listed below:
| Function | Description |
| AddressOf | Returns the memory address of an object. |
| Clone | Copies an object. |
| CreateInstance | Creates an instance of a class from its type identifier. |
| ObjectAt | Returns a reference for the object occupying the specified memory address. |
| TypeID | Returns the type identifier of a class. |