|Navigation: Language Reference > 4 - Entity Declarations > Complex Data Structures >====== CLASS (object declaration) ======|
|label||CLASS( [ parentclass ] )||[,EXTERNAL] [,IMPLEMENTS] [,DLL( )] [,STATIC] [,THREAD] [,BINDABLE]|
|[,MODULE( )] [, LINK( )] [, TYPE] [, DIM(dimension)] [,NETCLASS] [,PARTIAL]|
|[ data members and methods ]|
|CLASS||An object containing data members and methods that operate on the data.|
|parentclass||The label of a previously declared CLASS structure whose data and methods the new CLASS inherits. This may be a CLASS with the TYPE attribute.|
|EXTERNAL||Specify the object is defined, and its memory is allocated, in an external library.|
|IMPLEMENTS||Specify an INTERFACE for the CLASS. This adds additional methods to the implementation of the CLASS.|
|DLL||Specify the object is defined in a .DLL. This is required in addition to the EXTERNAL attribute.|
|STATIC||Specify the data members' memory is permanently allocated.|
|THREAD||Specify memory for all class variables are allocated once for each execution thread. Also causes Constructors and Destructors to be called on thread start and exit.|
|BINDABLE||Specify all variables in the class may be used in dynamic expressions.|
|MODULE||Specify the source code module containing the CLASS's member PROCEDURE definitions. This serves the same function as the MODULE structure within a MAP structure. If omitted, the member PROCEDURE definitions must all be in the same source code module containing the CLASS declaration.|
|LINK||Specify the source code module containing the CLASS's member PROCEDURE definitions is automatically added to the compiler's link list. This eliminates the need to specifically add the file to the project.|
|TYPE||Specify the CLASS is only a type definition and not also an object instance of the CLASS.|
|data members and methods||Data declarations and PROCEDURE prototypes. The data members may only be data declarations appropriate to a GROUP structure, and may include references to the same class (recursive classes). The WHAT and WHERE procedures allow access to the data members by their relative position within the CLASS structure.|
|DIM||Declares a CLASS as an array.|
|dimension||A positive numeric constant which specifies the number of elements in this dimension of the array.|
|NETCLASS||Switch off the generation of additional Clarion'specific code for the CLASS|
|PARTIAL||Identifies a CLASS definition that is split into more than one physical file|
A CLASS structure declares an object which contains data members (properties) and the methods (PROCEDUREs) that act on that data. A CLASS structure must be terminated by a period or the END statement.
Derived CLASSes (Inheritance)
A CLASS declared with the parentclass parameter creates a derived class which inherits all the data members and methods of the named parentclass. The derived class may also contain its own data members and methods.
All data members explicitly declared in the derived class create new variables, and cannot be declared with the same labels as data members in the parentclass.
Any method prototyped in the derived class with the same name as a method in the parentclass overrides the inherited method if both have the same parameter lists. If the two methods have different parameter lists, they create polymorphic functions in the derived class that must obey the rules of Procedure Overloading.
Object Properties (Encapsulation)
Each instance of a CLASS, whether a base class, derived class, or a declared instance of either, contains its own set of data members (properties) specific to that instance. These may be private or public. However, there is only one copy of any inherited methods (residing in the CLASS that declared it) which any instance of that CLASS, or any of its derived classes, calls.
The methods of a CLASS with the TYPE attribute cannot be directly called (as ClassName.Method)–they must be called only as a member methods of the objects declared as the type (as Object.Method).
VIRTUAL Methods (Polymorphism)
If there is a method prototyped in the CLASS with the same label as a method in the parentclass with the VIRTUAL attribute, it must also be prototyped with the VIRTUAL attribute in the derived class.
The VIRTUAL attribute on both prototypes creates virtual methods that allow the methods in a parentclass to call the same named VIRTUAL methods in the derived class to perform functions specific to the derived class that the parentclass does not know about.
VIRTUAL methods in the derived class may directly call the parentclass method of the same name by prepending PARENT to the method's name. This allows incremental derivation wherein a derived class method may simply call down to the parentclass method to perform its functionality, then extend it for the requirements of the derived class.
VIRTUAL methods in the derived class may optionally use the DERIVED attribute. This will cause the compiler to verify that a virtual method with the same name and calling signature exists in the base class. This prevents potential issues that can end up defining new virtual methods instead of overriding existing ones.
The scope of an object is dependent upon where it is declared. Generally, a declared object comes into scope at the CODE statement following its declaration and goes out of scope at the end of the related executable code section. A dynamically instantiated object (using NEW) shares the scope of the executable code section in which it is instantiated.
An Object declared:
·As Global data is in scope throughout the application.
·As Module data is in scope throughout the module.
·As Local data is in scope only in the procedure, except …
Methods prototyped in a derived CLASS declaration within a procedure's Local data section are Local Derived Methods and share the declaring procedure's scope for all local data declarations and routines. The methods must be defined within the same source module as the procedure within which the CLASS is declared and must immediately follow the procedure within that source–that is, they must come after any ROUTINEs and before any other procedures that may be in the same source module. This means the procedure's Local data declarations and ROUTINEs are all visible and can be referenced within these methods.
MyDerivedClass CLASS(MyClass) !Derived class with a virtual method
!SomeProc main executable code goes here
!SomeProc ROUTINEs goes here
!Routine code goes here
!MyDerivedClass methods immediately follow:
MyLocalVar = 10 !MyLocalVar is still in scope, and available for use
DO MyRoutine !MyRoutine is still in scope, and available for use
!Any other procedures in the same module go here, following all
!derived class methods
You declare an instance of a CLASS (an object) by simply naming the CLASS as the data type of the new instance, or by executing the NEW procedure in a reference assignment statement to a reference variable for that named CLASS. Either way, the new instance inherits all the data members and methods of the CLASS for which it is an instance. All the attributes of a CLASS except MODULE and TYPE are valid on an instance declaration.
If there is no TYPE attribute on the CLASS, the CLASS structure itself declares both the CLASS and an object instance of that CLASS. A CLASS with the TYPE attribute does not create an object instance of the CLASS.
For example, the following CLASS declaration declares the CLASS as a data type and an object of that type:
MyClass CLASS !Both a data type declaration and an object instance
while this only declares the CLASS as a data type:
MyClass CLASS,TYPE !Only a data type declaration
It is preferable to directly declare object instances as the CLASS data type rather than as a reference to the CLASS. This results in smaller quicker code and does not require you to use NEW and DISPOSE to explicitly create and destroy the object instance. The advantage of using NEW and DISPOSE is explicit control over the lifetime of the object. For example:
OneClass MyClass !Declared object instance, smaller and quicker
TwoClass &MyClass !Object reference, must use New and DISPOSE
!execute some code here
TwoClass &= NEW(MyClass) !The lifetime of the object starts here
!execute some code here
DISPOSE(TwoClass) ! and extends only to here
!execute some code here
Another advantage of declaring the object is the ability to declare the object with any of the attributes available for the CLASS declaration itself (except TYPE and MODULE). For instance, you can declare an object with the THREAD attribute, whether the CLASS is declared with THREAD or not.
The lifetime of an object depends on how it is instantiated:
·An object declared in the Global data section or a Module's data section is instantiated at the CODE statement following the PROGRAM statement and de-instantiated when the application terminates.
·A reference to an object is instantiated by the NEW statement, and de-instantiated by the DISPOSE statement.
·An object declared in a procedure's Local data section is instantiated at the CODE statement following the PROCEDURE statement and de-instantiated when a RETURN (implicit or explicit) executes to terminate the procedure.
The constructors and destructors for threaded classes are called for every thread. Every new thread gets new instances of CLASSes and variables declared at the global or module level with the THREAD attribute. The RTL calls constructors for the threaded classes when the thread is started and the destructors when the thread is ended. In previous Clarion versions they were called only when the main thread started and ended.
Data (Property) Initialization
The simple data type data members of an object are automatically allocated memory and initialized to blank or zero (unless the AUTO attribute is specified) when the object comes into scope. The allocated memory is returned to the operating system when the object goes out of scope.
The reference variable data members of an object are not allocated memory and are not initialized when the object comes into scope–you must specifically execute a reference assignment or a NEW statement. These references variables are not automatically cleared when the object goes out of scope, so you must DISPOSE of all NEWed properties before the object goes out of scope.
Constructors and Destructors
A CLASS method labelled “Construct” is a constructor method which is automatically invoked when the object comes into scope, immediately after the data members of the object are allocated and initialized.
The “Construct” method may not receive any parameters and may not use the VIRTUAL attribute. You may explicitly call the “Construct” method in addition to its automatic invocation.
Multiple Constructors with parameters are now supported.
If an object is an instance of a derived CLASS and both the parentclass and the derived CLASS contain constructors and the derived CLASS's constructor does not have the REPLACE attribute, then the parentclass constructor is automatically invoked at the beginning of the derived CLASS's constructor. If the derived CLASS's constructor does have the REPLACE attribute, then only derived CLASS's constructor is automatically invoked (the derived CLASS's constructor method can explicitly call PARENT.Construct if it needs to).
A CLASS method labelled “Destruct” is a destructor method which is automatically invoked when the object leaves scope, immediately before the data members of the object are de-allocated. The “Destruct” method may not receive any parameters. You may explicitly call the “Destruct” method in addition to its automatic invocation.
If an object is an instance of a derived CLASS and both the parentclass and the derived CLASS contain destructors and the derived CLASS's destructor does not have the REPLACE attribute, then the parentclass destructor is automatically invoked at the end of the derived CLASS's destructor. If the derived CLASS's destructor does have the REPLACE attribute, then only derived CLASS's destructor is automatically invoked (the derived CLASS's destructor method can explicitly call PARENT.Destruct if it needs to).
Public, PRIVATE, and PROTECTED (Encapsulation)
Public data members and methods of a CLASS or derived CLASS are declared without either the PRIVATE or PROTECTED attributes. Public data members and methods are visible to all the methods of the declaring CLASS, and derived CLASSes, and any code where the object is in scope.
Private data members and methods are declaredwith the PRIVATE attribute. Private data members and methods are visible only to the methods of the CLASS within which they are declared and any other procedures contained in the same source code module.
Protected data members and methods are declared with the PROTECTED attribute. Protected data members and methods are visible only to the methods of the CLASS within which they are declared, and to the methods of any CLASS derived from the CLASS within which they are declared.
The PROCEDURE definition of a method (its executable code, not its prototype) is external to the CLASS structure. The method's definition must either prepend the label of the CLASS to the label of the PROCEDURE, or name the CLASS (and label it SELF) as the first (implicit) parameter in the list of parameters passed in to the PROCEDURE.
Remember that on the PROCEDURE definition statement you are assigning labels for use within the method to all the passed parameters, and so, since the CLASS's label is the data type of the implicit first parameter, you must use SELF as the assigned label for the CLASS name parameter. For example, for the following CLASS declaration:
MyProc PROCEDURE(LONG PassedVar) !The method takes 1 parameter
you may define the MyProc PROCEDURE either as:
MyClass.MyProc PROCEDURE(LONG PassedVar) !Prepend the CLASS name to
CODE !the method's label
MyProc PROCEDURE(MyClass SELF, LONG PassedVar) !The CLASS name is the
! implicit first parameter's data type, labeled SELF
Referencing an Object's properties and methods in your code
You must reference the data members of a CLASS using Clarion's Field Qualification syntax. To do this, you prepend the label of the CLASS (if it is an object instance of itself) or the label of an object instance of the CLASS to the label of the data member.
For example, for the following CLASS declarations:
MyClass CLASS !Without TYPE, this is also an object instance
MyField LONG ! in addition to a class type declaration
MyClass2 MyClass !Declare another object instance of MyClass
you must reference the two MyField variables from procedures external to the object as:
MyClass.MyField = 10 !References the MyClass CLASS declaration's object
MyClass2.MyField = 10 !References the MyClass2 declaration's object
You may call the methods of a CLASS either using Field Qualification syntax (by prepending the label of the CLASS to the label of the method), or by passing the label of the CLASS as the first (implicit) parameter in the list of parameters passed to the PROCEDURE.
For example, for the following CLASS declaration:
you may call the MyProc PROCEDURE either as:
SELF and PARENT
Within the methods of a CLASS, the data members and methods of the current object's instance are referenced with SELF prepended to their labels instead of the name of the CLASS. This allows the methods to generically reference the data members and methods of the currently executing instance of the CLASS, without regard to whether it is executing the parentclass, a derived class, or any instance of either. This is also the mechanism that allows a parentclass to call virtual methods of a derived class.
For example, expanding on the previous example, MyField is referenced within the MyClass.MyProc method as:
SELF.MyField = 10 !Assign to the current object instance's property
The data members and methods of a parentclass can be directly referenced from within the methods of a derived class with PARENT prepended to their labels instead of SELF.
!execute some code
PARENT.MyProc !Call the base class method
!execute some more code
Methods of derived CLASSes cannot have a formal parameter with the label PARENT, and additionally no method can have an explicit formal parameter with the label SELF.
MyDerivedClass.MyProc PROCEDURE(MyDerivedClass Parent) !Illegal use of Parent
The compiler produces a “Redefining system intrinsics” warning for any explicit attempt to declare any data with the labels SELF or PARENT used in this context.
In Clarion .Net nested classes are supported. You can declare a class inside of another class. The only restriction is that only inline methods, properties, and indexers can be used in the nested class. See also INLINE.
CLASS Conceptual Example:
MAP. !MAP required to get BUILTINS.CLW
OneClass CLASS !Base class
NameGroup GROUP !Reference as OneClass.NameGroup
First STRING(20) !reference as OneClass.NameGroup.First
Last STRING(20) !reference as OneClass.NameGroup.Last
BaseProc PROCEDURE(REAL Parm) !Declare method prototype
Func PROCEDURE(REAL Parm),STRING,VIRTUAL !Declare virtual method prototype
Proc PROCEDURE(REAL Parm),VIRTUAL !Declare virtual method prototype
END !End CLASS declaration
TwoClass CLASS(OneClass),MODULE('TwoClass.CLW') !Derived from OneClass
Func PROCEDURE(LONG Parm),STRING !replaces OneClass.Func
Proc PROCEDURE(STRING Msg,LONG Parm) !Functionally overloaded
ClassThree CLASS(TwoClass),MODULE('Class3.CLW') !Derived from TwoClass
Func PROCEDURE(<;STRING Msg>,LONG Parm),STRING,VIRTUAL
Proc PROCEDURE(REAL Parm),VIRTUAL
ClassFour ClassThree !Declare an instance of ClassThree
ClassFive ClassThree !Declare an instance of ClassThree
OneClass.NameGroup = '|OneClass Method' !Assign values to each instance of NameGroup
TwoClass.NameGroup = '|TwoClass Method'
ClassThree.NameGroup = '|ClassThree Method'
ClassFour.NameGroup = '|ClassFour Method'
MESSAGE(OneClass.NameGroup & OneClass.Func(1.0))!Calls OneClass.Func
MESSAGE(TwoClass.NameGroup & TwoClass.Func(2)) !Calls TwoClass.Func
MESSAGE(ClassThree.NameGroup & ClassThree.Func('|Call ClassThree.Func',3.0))
MESSAGE(ClassFour.NameGroup & ClassFour.Func('|Call ClassFour.Func',4.0))
!Also Calls ClassThree.Func
OneClass.BaseProc(5) !BaseProc Calls OneClass.Proc & Func
BaseProc(TwoClass,6) !BaseProc Also calls OneClass.Proc & Func
TwoClass.Proc('Second Class',7) !Calls TwoClass.Proc (overloaded)
ClassThree.BaseProc(8) !BaseProc Calls ClassThree.Proc & Func
ClassFour.BaseProc(9) !BaseProc Also Calls ClassThree.Proc & Func
Proc(ClassFour,'Fourth Class',10) !Calls TwoClass.Proc (overloaded)
OneClass.BaseProc PROCEDURE(REAL Parm) !Definition of OneClass.BaseProc
MESSAGE(Parm & SELF.NameGroup &'|BaseProc executing|calling SELF.Proc Virtual method')
SELF.Proc(Parm) !Calls virtual method
MESSAGE(Parm & SELF.NameGroup&'|BaseProc executing|calling SELF.Func Virtual method')
MESSAGE(SELF.NameGroup & SELF.Func(Parm)) !Calls virtual method
OneClass.Func PROCEDURE(REAL Parm) !Definition of OneClass.Func
RETURN('|Executing OneClass.Func - ' & Parm)
Proc PROCEDURE(OneClass SELF,REAL Parm) !Definition of OneClass.Proc
MESSAGE(SELF.NameGroup & ' |Executing OneClass.Proc - ' & Parm)
!The TwoClass.CLW file contains:
Func PROCEDURE(TwoClass SELF,LONG Parm) !Definition of TwoClass.Func
RETURN('|Executing TwoClass.Func - ' & Parm)
TwoClass.Proc PROCEDURE(STRING Msg,LONG Parm) !Definition of TwoClass.Proc
MESSAGE(Msg & '|Executing TwoClass.Proc - ' & Parm)
!The Class3.CLW file contains:
ClassThree.Func PROCEDURE(<;STRING Msg>,LONG Parm) !Definition of ClassThree.Func
SELF.Proc(Msg,Parm) !Call TwoClass.Proc (overloaded)
RETURN(Msg & '|Executing ClassThree.Func - ' & Parm)
ClassThree.Proc PROCEDURE(REAL Parm) !Definition of ClassThree.Proc
SELF.Proc('Called from ClassThree.Proc',Parm) !Call TwoClass.Proc
MESSAGE(SELF.NameGroup &' |Executing ClassThree.Proc - ' & Parm)