At this point, I can end my review of sets of simple functions, and move on to describing the object part of KOL. Objects in KOL take advantage of almost all the basic delights of object programming, namely encapsulation, inheritance, and polymorphism, although sometimes somewhat limited.
For example, as I said before, inheritance should not be overused when building an object hierarchy, since each object type (or class) will require its own virtual method table in memory. Therefore, I took great care in building my hierarchy of objects.
The base object type for all objects in KOL is TObj. For some reason, later (in version 0.93 of 08/25/2001) the _TObj object type was introduced, from which TObj is inherited. The main reason was that with each modification of the TObj type, the pointer to the table of virtual methods vmt was shifted. Creation of a semi-dummy "ancestor" for TObj ensured the constancy of the vmt field in the object structure - at offset 0, and made it possible to create the InstanceSize function that returns the size of the field structure in memory for any object inherited from TObj. In addition, for each call to vmt, the code is shorter by 1 byte in this case. The author of this modification is Vyacheslav Gavrik, for which he is undoubtedly grateful.
So, what is TObj (I will consider its methods together with the methods of its ancestor _TObj). To some extent, this is an analogue of the TObject class in the VCL, and at the same time, it can also be considered an analogue of the TComponent. Almost all other object types, with a few exceptions, are derived directly from TObj. The _TObj object defines the only (first) virtual Init method, and there is one more function VmtAddr, and this is where the list of its methods ends (it has no fields of its own). Since _TObj is a helper object whose only purpose is to make code smaller, there is no need to directly use it in your program.
The TObj object is already more complicated, the virtual destructor Destroy already appears in it (but you should always call the Free method), it contains the fAutoFree list (of the PList type, by the way, the presence of a reference to PList in TObj already means that at least some methods of the object type TList will be included in the code of any KOL program, but I decided to go for it, since it is difficult to do anything without lists at all, i.e. the list will still get into the code of even a minimal program). fAutoFree is a list of objects that will be automatically destroyed along with the data, there are methods for adding objects for self-destruction (Add2AutoFree and Add2AutoFreeEx) when the destructor is executed. The TObj type (and therefore all object types in KOL) also has an OnDestroy event.
There is a Tag field (yes, in KOL it is defined at the lowest level of the hierarchy, i.e. any object in KOL has this field a priori).
There is even an object usage counter, which allows you to prevent the destruction of the object while someone else needs it: calls to RefInc increase the counter, and for an object with a nonzero counter, calling the destructor will not lead to any consequences (except for marking that the destructor was called). When decreasing the usage counter by calling RefDec to zero, it is checked whether the destructor was called, and if it was, the object is destroyed, this time finally. The RefCount field is available for analysis from the program (the least significant bit of this field is used as an indication that the destructor was called, all the others are a counter that increases by 2 with each call to RefInc, and decreases by 2 with each RefDec).
In KOL itself, the RefInc and RefDec methods are applied “just in case” when processing messages for a visual object (“just in case” is that the object can be destroyed while any window message is being processed for it, and then it would be almost inevitable crash of the application). In fact, the RefInc methods can be used in multithreaded applications to protect temporary objects managed from different threads for a period of active use in some part of the code.
Sometimes it becomes necessary to "simultaneously" destroy an object and zero (assign nil) to the pointer to this object. The VCL has a function for this, in KOL the function is called Free_And_Nil for the same purpose. Moreover, in this function, the object pointer variable is first reset to zero, and only then the object is destroyed (by calling the Free method). Of course, this is almost equivalent to having the object first destroyed and then assigned nil to a pointer variable. But in a multithreaded application, the difference can be felt. It is enough to imagine a situation in which the object was destroyed (or began to decay, but the operation has not been completed yet), and the pointer is still not equal to nil, and at that moment the threads switched, and some operations with the same object through the same pointer. Even in the case of a single-threaded application, the fact that some global pointer continues to point to a non-existent object, or to an object, for which the destruction operation has already begun, presents a certain danger. So the need for the Free_And_Nil function is obvious.
In addition to the listed properties, TObj has a string field named Name, added by popular demand. But this field is optional, and the compiler knows that such a field exists only when the Use_Names conditional compilation symbol is included in the project options. In this case, all named objects are remembered in the list of the parent object, and can be found by calling its FindObj (s) method.
The TObj object type is not intended to create its own instances, it was designed precisely as an ancestor for all inherited object types. Non-visual objects should be inherited from it, which must have a destructor, or can be passed as a parameter wherever a variable of type PObj is required. In all other cases, when the functionality of the TObj object is not required, you can create your own objects that do not derive from TObj. But the product of descendants from TObj is actually quite inexpensive, so my advice is to inherit all simple objects from TObj in general.