| **Navigation:**  [[introduction.htm|Language Reference]] > 4 - Entity Declarations > Complex Data Structures >====== CLASS (object declaration) ====== | [[group compound data structure .htm|{{btn_prev_n.gif|Previous page}}]][[introduction.htm|{{btn_home_n.gif|Return to chapter overview}}]][[class object declaration .htm#selfparent|{{btn_next_n.gif|Next page}}]] | | || | label | **CLASS( **[ //parentclass// ]** )** | [,**EXTERNAL**] [,**IMPLEMENTS**] [,**DLL( )**] [,**STATIC**] [,**THREAD**] [,**BINDABLE**] | | | | [,**MODULE( )**] [, **LINK( )**] [, **TYPE**] [, **DIM**(//dimension//)] [,**NETCLASS**] [,**PARTIAL**] | | | [ //data members and methods //] | | | | **END** | | {{blk2blue.jpg|blk2blue.jpg}} | **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 set variable defined externally in dll .htm|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 class link into project .htm|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.{{newcnet.jpg|NewCNet.jpg}} | | //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{{newcnet.jpg|NewCNet.jpg}} | | **PARTIAL** | Identifies a CLASS definition that is split into more than one physical file{{newcnet.jpg|NewCNet.jpg}} | 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 set virtual method .htm|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 [[class object declaration .htm#selfparent|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 prevent function overloading .htm|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. **Scoping Issues** 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. **Example:** **SomeProc    PROCEDURE** **MyLocalVar  LONG** **MyDerivedClass CLASS(MyClass)  !Derived class with a virtual method** **MyProc          PROCEDURE,VIRTUAL** **               END** ** CODE** ** !SomeProc main executable code goes here** ** !SomeProc ROUTINEs goes here ** **MyRoutine ROUTINE** ** !Routine code goes here** ** !MyDerivedClass methods immediately follow:** **MyDerivedClass.MyProc PROCEDURE** ** CODE** ** 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** **Instantiation** 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** **MyField   LONG** **MyProc    PROCEDURE** **         END** while this only declares the CLASS as a data type: **MyClass  CLASS,TYPE  !Only a data type declaration ** **MyField   LONG** **MyProc    PROCEDURE** **         END** 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: **MyClass  CLASS,TYPE** **MyField   LONG** **MyProc    PROCEDURE** **         END** **OneClass MyClass           !Declared object instance, smaller and quicker** **TwoClass &MyClass          !Object reference, must use New and DISPOSE** ** CODE** ** !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. **Threading** {{notebox.jpg|NoteBox.jpg}} 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. {{blk2blue.jpg|blk2blue.jpg}} {{newc7.jpg|NewC7.jpg}} 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. {{newcnet.jpg|NewCNet.jpg}} Multiple Constructors with parameters are now supported. {{blk2blue.jpg|blk2blue.jpg}} 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. **Method Definition** 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: **MyClass CLASS** **MyProc   PROCEDURE(LONG PassedVar)             !The method takes 1 parameter** **        END** you may define the MyProc PROCEDURE either as: **MyClass.MyProc PROCEDURE(LONG PassedVar)       !Prepend the CLASS name to ** ** CODE                                          !the method's label** or as: **MyProc PROCEDURE(MyClass SELF, LONG PassedVar) !The CLASS name is the** **CODE** **! 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** **MyProc    PROCEDURE** **         END** **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: **MyClass  CLASS** **MyProc    PROCEDURE** **         END** you may call the MyProc PROCEDURE either as: **CODE** **MyClass.MyProc** or as: **CODE** **MyProc(MyClass)** **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: **MyClass.MyProc PROCEDURE** **CODE** **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. For example: **MyDerivedClass.MyProc PROCEDURE** **CODE** **!execute some code** **PARENT.MyProc       !Call the base class method** **!execute some more code** {{notebox.jpg|NoteBox.jpg}} 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. For example: **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. __**Nested CLASSes**__ {{newcnet.jpg|NewCNet.jpg}} 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 embed source code in a class .htm|INLINE]]. **CLASS Conceptual Example:** **      PROGRAM** **      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 ** **          END ** **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** **          END** **ClassThree CLASS(TwoClass),MODULE('Class3.CLW')  !Derived from TwoClass** **Func        PROCEDURE(<;STRING Msg>,LONG Parm),STRING,VIRTUAL** **Proc        PROCEDURE(REAL Parm),VIRTUAL** **           END** **ClassFour  ClassThree                            !Declare an instance of ClassThree** **ClassFive  ClassThree                            !Declare an instance of ClassThree** ** CODE** ** 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))** **!Calls ClassThree.Func** ** 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** ** CODE** ** 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** ** CODE** ** RETURN('|Executing OneClass.Func - ' & Parm)** **Proc       PROCEDURE(OneClass SELF,REAL Parm)   !Definition of OneClass.Proc** ** CODE** ** MESSAGE(SELF.NameGroup & ' |Executing OneClass.Proc - ' & Parm)** {{black.jpg|black.jpg}}** ** **  !The TwoClass.CLW file contains:** **  MEMBER('ClassPrg')** **Func       PROCEDURE(TwoClass SELF,LONG Parm)      !Definition of TwoClass.Func** ** CODE** ** RETURN('|Executing TwoClass.Func - ' & Parm)** **TwoClass.Proc  PROCEDURE(STRING Msg,LONG Parm)     !Definition of TwoClass.Proc** ** CODE** ** MESSAGE(Msg & '|Executing TwoClass.Proc - ' & Parm)** {{black.jpg|black.jpg}}** ** **  !The Class3.CLW file contains:** **  MEMBER('ClassPrg')** **ClassThree.Func  PROCEDURE(<;STRING Msg>,LONG Parm) !Definition of ClassThree.Func** ** CODE** ** SELF.Proc(Msg,Parm)                               !Call TwoClass.Proc (overloaded)** ** RETURN(Msg & '|Executing ClassThree.Func - ' & Parm)** **ClassThree.Proc  PROCEDURE(REAL Parm)              !Definition of ClassThree.Proc** ** CODE** ** SELF.Proc('Called from ClassThree.Proc',Parm)     !Call TwoClass.Proc** ** MESSAGE(SELF.NameGroup &' |Executing ClassThree.Proc - ' & Parm)** **See Also:** [[virtual set virtual method .htm|VIRTUAL]] [[derived prevent function overloading .htm|DERIVED]] [[field qualification.htm|Field Qualification]] [[module specify member source file .htm|MODULE]] [[procedure prototypes.htm|PROCEDURE Prototypes]] [[procedure overloading.htm|Procedure Overloading]] [[what return field from group .htm|WHAT]] [[where return field position in group .htm|WHERE]]