Statement CLASS Foundation
Class declaration
[STATIC|FREEZE|FINAL] CLASS <ClassName> ;
[FROM <SuperClass,...>] ;
[SHARING <SuperClass,...>]
[CLASS METHOD initClass]
[ METHOD init]
<class declaration statements>
ENDCLASS
The statement CLASS starts the declaration of a class. Following the class definition are the declarations of the member variables and methods which are available to instances of the class. The class declaration ends with the statement ENDCLASS. Following the declaration section, the methods declared in the class must be coded. (See: METHOD (Implementation)).
The class declaration CLASS...ENDCLASS consists entirely of four parts:
The class function has the function name <ClassName> and returns a reference to the class object. The class object represents the class and using the class method :new(), generates instances (objects) of the class. The word "new" is reserved and cannot be used as the name of a method or an instance variable. Other reserved identifiers used for methods in all classes are listed in the table below:
Method | Description |
---|---|
:className() | Returns the class name as a character string |
:classObject() | Returns the class object of an instance |
:isDerivedFrom() | Determines whether or not an object is derived from a particular class :isDerivedFrom(<cClassName> | <oClassObject>) --> .T.|.F. |
:new() | Creates instances of a class |
When the class declaration uses the optional statement STATIC CLASS, the class function is a STATIC FUNCTION and cannot be called outside the source code file within which the class is declared.
A class may be produced from previously declared classes. The names of the declared classes must be indicated by the statement CLASS with the option FROM as a comma separated list <SuperClass,...>. These classes are superclasses, and the new class is a subclass of the superclasses. All classes that are not declared as STATIC, may be used as superclasses, including classes contained in the Xbase++ language. The new class <ClassName> inherits the entire structure of the superclasses. In this way, the new class has access to all member variables and methods of the superclasses which are not declared with the HIDDEN: visibility attribute within the superclasses.
The option FROM defines all classes whose member variables and methods are to be inherited by the new class. If class variables exist in the superclasses, all the class variables of the superclasses which are not declared SHARED are added to the class <ClassName>. When SHARED is not used, the new class receives its own class variables with the same names as those in the superclasses. In this case the new class cannot access the same class variables as the superclasses. The new class has, effectively, received copies of the class variables of the superclasses and accesses its own values.
The option SHARING is significant only when the new class <ClassName> inherits from two or more classes <SuperClass,...>which themselves have a common superclass. The following example should help explain this relationship: Assume a class A with an instance variable iVarA. Classes B and C are subclasses of class A (and thus each include a copy of iVarA). Finally a class D is declared which is derived from classes B and C. When the SHARING option is used in the class D declaration, both of the inherited copies of the instance variable iVarA in classB and C are brought together as a single instance variable in class D. Class D then has only one copy of instance variable iVarA.
The following statements (<class declaration statements>) are used in declaring member variables and methods:
Statement | Meaning | |
---|---|---|
HIDDEN: | Only visible within methods of this class | |
PROTECTED: | Only visible within methods of this class and its subclasses | |
EXPORTED: | Visible to the entire application (globally visible) | |
CLASS VAR | ..[IS]..[IN] | Declaration of a class variable |
VAR | ..[IS]..[IN] | Declaration of an instance variable |
CLASS METHOD | ..[IS]..[IN] | Declaration of a class method |
METHOD | ..[IS]..[IN] | Declaration of an instance method |
By default, all of the instance variables and methods declared in the class are treated as having HIDDEN: visibility. When a member variable or method is HIDDEN:, it is not visible outside the methods of the class. When the visibility attribute PROTECTED: or EXPORTED: is used, all subsequent member variables and methods declared assume this visibility until a different visibility attribute is used. In addition to the keywords affecting visibility, member variables can assume separate attributes that control the ability to assign values (write access). More information on visibility may be found in the sections discussing the respective attributes.
Details for the declaration of member variables and methods are found in the sections covering the corresponding statements.
// The program example shows the basic procedure for
// declaring a class and programming its methods.
// The label class supports the ability to display a value
// on the screen at specific coordinates and in a specific
// color, and includes instance variables for the coordinates and color.
// The only method is called Show(), in which the command SAY is
// executed. The label class is used in a later program example.
**********************************************************
* Label Class *
**********************************************************
//
// Class declaration
//
CLASS Label
EXPORTED: // Globally visible
VAR nRow, nCol, cLabel, cColor
METHOD init, Show
ENDCLASS
//
// Initialize object with default values, if no
// arguments are passed to Label():new()
//
METHOD Label:init( nRow, nCol, cLabel, cColor )
::nRow := IIF( nRow == NIL, Row(), nRow )
::nCol := IIF( nCol == NIL, Col(), nCol )
::cLabel := IIF( cLabel==NIL, "" , cLabel )
::cColor := IIF( cColor==NIL, SetColor(), cColor )
RETURN self
//
// Display label
//
METHOD Label:Show
@ ::nRow, ::nCol SAY ::cLabel COLOR ::cColor
RETURN self
// This class implements the Cursor class and
// shows the procedure for initializing the class object
// using the method :initClass() and assigning default values
// to class variables. An instance of the Cursor class
// can control the position and form of the screen cursor.
// The Cursor class is used in another program.
**********************************************************
* Cursor Class. *
**********************************************************
#include "setcurs.ch"
//
// Class declaration including declaring class variables and methods
//
CLASS Cursor
EXPORTED: // Globally visible
CLASS VAR nMaxRow, nMaxCol READONLY SHARED
// Assignment possible only in
// methods.
// All subclasses have equal
// access
CLASS METHOD initClass, Hide, SetMode
VAR nRow, nCol, nShape
METHOD init, Show, UpdatePos
ENDCLASS
//
// Initialize the class object
//
CLASS METHOD Cursor:initClass()
::nMaxRow := MaxRow()
::nMaxCol := MaxCol()
RETURN self
//
// Initialize instance variables of the object with default values
// when no arguments are passed to the Cursor():new() (which
// passes any arguments on to Cursor:init() automatically).
//
METHOD Cursor:init( nRow, nCol, nShape )
::nRow := IIF( nRow ==NIL, Row(), nRow )
::nCol := IIF( nCol ==NIL, Col(), nCol )
::nShape := IIF( nShape==NIL, SetCursor(), nShape )
RETURN self
//
// Example of a class method
//
CLASS METHOD Cursor:SetMode( nMaxRow, nMaxCol )
IF SETMODE( nMaxRow, nMaxCol )
::initClass() // self is the class object
ENDIF
RETURN self
//
// Turn off the cursor. The method is declared as a CLASS METHOD,
// since no access to instance variables is required.
//
CLASS METHOD Cursor:Hide
SetCursor( SC_NONE )
RETURN self // self is the class object
//
// Turn the cursor on and reposition
//
METHOD Cursor:Show( nRow, nCol )
IF ValType( nRow ) == "N"
::nRow := nRow
ENDIF
IF ValType( nCol ) == "N"
::nCol := nCol
ENDIF
SetPos( ::nRow, ::nCol )
SetCursor( ::nShape )
RETURN self
//
// Synchronize the cursor position to actual screen position
//
METHOD Cursor:UpdatePos
::nRow := Row()
::nCol := Col()
RETURN self
// In this example, the box class is coded to override all the
// instance variables and methods of the classes from the
// previous two examples. The box class inherits from the cursor
// and label classes and is a simple example of multiple inheritance.
// Its functionality is a combination of the functions DispBox(),
// SaveScreen() and RestScreen(). Movement methods are provided
// which allow a box to move interactively on the screen. The
// functionality of the label class is used to display a heading
// on the border of a box. The cursor class is used to display
// the screen cursor only within the border of a box.
**********************************************************
* Box class is created from two classes *
* Cursor Label *
* \ / *
* Box *
**********************************************************
#include "setcurs.ch"
#define IS_NEW 0 // Constants for ::nStatus
#define IS_HIDDEN 1
#define IS_VISIBLE 2
//
// Class declaration using FROM
//
CLASS Box FROM Cursor, Label // Inherits from two classes
EXPORTED: // Globally visible
VAR nTop, nLeft, nBottom, nRight, cBox
VAR cScreen, nStatus READONLY // READONLY -> Assignment only
// allowed from within Box class
// and its subclasses.
VAR nCursorRow IS nRow IN Cursor // New name since nRow and nCol
VAR nCursorCol IS nCol IN Cursor // exist in both superclasses
// "Cursor"and "Label"
VAR nLabelRow IS nRow IN Label
VAR nLabelCol IS nCol IN Label
METHOD init, Show, Hide, Say // Initialization and display
METHOD Up, Down, Left, Right // movement methods
HIDDEN: // Only visible in Box class
METHOD Savescreen, Restscreen // Save and restore the screen
ENDCLASS
//
// Initialize box object. The arguments passed
// to the :new() method are passed to the :init() method.
// See the next program example.
//
METHOD Box:init( nTop, nLeft , nBottom, nRight, ;
cBox, cColor, cTitle )
::nTop := nTop
::nLeft := nLeft
::nBottom := nBottom
::nRight := nRight
::cBox := cBox
::nStatus := IS_NEW // Initialize instance variable
// inherited from the Cursor
// class
::Cursor:init( nTop+1, nLeft+1, SC_NORMAL )
// Constant from Setcurs.ch
*------------------ Alternative Initialization method---------------*
* ::nCursorRow := nTop +1 // Declared in Cursor class and *
* ::nCursorCol := nLeft+1 // redeclared in the Box class *
* ::nShape := SC_NORMAL // Declared in Cursor class *
*-------------------------------------------------------------------*
IF ValType( cTitle ) <> "C" // Validate the title (headline)
cTitle := "" // and center
ENDIF
nLeft := nLeft+ Round((nRight-nLeft-LEN(cTitle)+1)/2 ,0)
// Initialize instance variables
// inherited from the Label class
::Label:init( nTop, nLeft, cTitle, cColor )
*------------------ Alternative Initialization method-------------*
* ::nLabelRow := nTop // Declared in Label class and*
* ::nLabelCol := nLeft // redeclared in the Box class*
* ::cLabel := cTitle // Declared in the Label class*
* ::cColor := cColor // Declared in the Label class*
*-----------------------------------------------------------------*
RETURN self
//
// Display the box
//
METHOD Box:Show
LOCAL cBackGround
IF ::nStatus == IS_NEW // Box was never displayed
::SaveScreen() // Save what will be overwritten
DispBox( ::nTop , ::nLeft , ;
::nBottom, ::nRight, ;
::cBox , ;
::cColor ) // cColor is declared in the
// Label class
ELSEIF ::nStatus == IS_HIDDEN // Box is hidden
cBackGround := ; // Save what will be overwritten
SaveScreen( ::nTop, ::nLeft, ::nBottom, ::nRight )
::RestScreen() // restore screen
::cScreen := cBackGround // save background
ENDIF
::nStatus := IS_VISIBLE // save status
::Label :Show() // Display title and cursor, by
// calling show methods from
::Cursor:Show() // Label and Cursor classes
RETURN self
//
// Hide the box
//
METHOD Box:Hide
LOCAL cForeGround
IF ::nStatus == IS_VISIBLE
::Cursor : Hide() // Turn off cursor
// Hide() is in the Cursor class
cForeGround := ; // Save foreground
SaveScreen( ::nTop, ::nLeft, ::nBottom, ::nRight )
::RestScreen() // Restore screen we overwrote
::cScreen := cForeGround // save foreground
::nStatus := IS_HIDDEN // save status
ENDIF
RETURN self
//
// Display a value with the box using relative coordinates.
// (0,0 corresponds to nTop+1 and nLeft+1).
// Scroll the contents of the box when the row passed
// is greater than the lowest line in the box.
//
METHOD Box:Say( nRow, nCol, xValue )
LOCAL cOldColor := SetColor( ::cColor )
IF ::nStatus == IS_VISIBLE .AND. PCount() == 3
nRow += ::nTop + 1 // Figure in absolute
nCol += ::nLeft + 1 // coordinates
IF nRow >= ::nBottom // last line is reached
::nCursorRow := nRow := ::nBottom-1
Scroll( ::nTop+1, ::nLeft+1, ::nBottom-1, ::nRight-1, 1 )
ENDIF
IF nCol >= ::nRight // last column is reached
::nCursorCol := nCol := ::nRight-1
ENDIF
@ nRow, nCol SAY PADR( xValue, ::nRight-nCol )
::Cursor:UpdatePos() // method of the Cursor class
ENDIF
SetColor( cOldColor )
RETURN self
//
// Shift box one line up
//
METHOD Box:Up
IF ::nStatus == IS_VISIBLE .AND. ::nTop > 0
DispBegin()
::Hide() // Instance variables:
::nTop-- // - in Box Class
::nBottom-- // - in Box Class
::nCursorRow-- // - nRow in Cursor Class
::nLabelRow-- // - nRow in Label Class
::Show()
DispEnd()
ENDIF
RETURN self
//
// Shift box one line down
//
METHOD Box:Down
IF ::nStatus == IS_VISIBLE .AND.;// SHARED class variable
::nBottom < ::nMaxRow // nMaxRow is in the Cursor class
DispBegin()
::Hide() // Instance variables:
::nTop++ // - in Box class
::nBottom++ // - in Box class
::nCursorRow++ // - nRow in Cursor class
::nLabelRow++ // - nRow in Label class
::Show()
DispEnd()
ENDIF
RETURN self
//
// Shift box one column to the left
//
METHOD Box:Left
IF ::nStatus == IS_VISIBLE .AND. ::nLeft > 0
DispBegin()
::Hide() // Instance variables:
::nLeft-- // - in Box class
::nRight-- // - in Box class
::nCursorCol-- // - nCol in Cursor class
::nLabelCol-- // - nCol in Label class
::Show()
DispEnd()
ENDIF
RETURN self
//
// Shift box one column to the right
//
METHOD Box:Right
IF ::nStatus == IS_VISIBLE .AND.;// SHARED class variable
::nRight < ::nMaxCol // nMaxCol is in the Cursor class
DispBegin()
::Hide() // Instance variables:
::nLeft++ // - in Box class
::nRight++ // - in Box class
::nCursorCol++ // - nCol in Cursor class
::nLabelCol++ // - nCol in Label class
::Show()
DispEnd()
ENDIF
RETURN self
//
// Save screen image
//
METHOD Box:Savescreen
::cScreen := SaveScreen( ::nTop, ::nLeft, ::nBottom, ::nRight )
RETURN self
//
// Display screen image again
//
METHOD Box:Restscreen
RestScreen( ::nTop, ::nLeft, ::nBottom, ::nRight, ::cScreen )
RETURN self
// This example demonstrates using the Box class. A box object is
// produced, and then the user is allowed to interactively move it
// around the screen. In addition to showing the Box class, this
// example demonstrates the difference in style between procedural
// and object-oriented programming. This example demonstrates the
// object-oriented style. The next example demonstrates the procedural
// style.
**********************************************************
* BOXOOP.PRG *
* Test program for the Box class *
**********************************************************
//
// Move box with the arrow keys on the screen and
// indicate Inkey() code within the box.
//
#include "Box.ch"
#include "Inkey.ch"
PROCEDURE Test
LOCAL oBox, nKey := 0, nRow := 0, nCol := 0
// Generate box object
oBox := Box():new( 10, 10, 20, 40, ;
"B_THIN", "W+/BG", "Heading" )
oBox:show()
oBox:say( 0, 0, "Press a key,..." )
oBox:say( 1, 0, "Arrow keys move the box" )
oBox:say( 2, 0, "Escape key exits program" )
nRow := 3
DO WHILE nKey <> K_ESC
nKey := Inkey(0)
DO CASE
CASE nKey == K_UP ; oBox:up()
CASE nKey == K_LEFT ; oBox:left()
CASE nKey == K_DOWN ; oBox:down()
CASE nKey == K_RIGHT ; oBox:right()
ENDCASE
oBox:say( nRow, nCol, ;
"The Inkey() Code was:" + LTrim(Str(nKey)) )
nRow ++
nCol ++
IF oBox:nCursorCol >= oBox:nRight-1
nCol := 0
ENDIF
ENDDO
oBox:hide()
RETURN
// This example demonstrates the differences in style between
// procedural and object-oriented programming.
// The example demonstrates the procedural style.
// One who has not worked with OOP may compare
// the function BoxUp() in this example with the method up()
// or the call to oBox:up() from the previous two examples. It
// becomes clear that the values which are contained in
// instance variables of a box object, are kept in
// LOCAL variables in this example.
**********************************************************
* BOXPROC.PRG *
**********************************************************
//
// Move box with the arrow keys on the screen and
// indicate Inkey() code within the box.
//
#include "Box.ch"
#include "Inkey.ch"
PROCEDURE Test
LOCAL cScreen , nKey := 0, nRow := 0, nCol := 0
LOCAL nTop :=10, nLeft:=10, nBottom:=20, nRight:=40
// Show box and save screen
cScreen := BoxShow( nTop, nLeft, nBottom, nRight, ;
B_THIN, "W+/BG", "Heading" )
BoxSay( 0, 0, nTop, nLeft, nBottom, nRight, ;
"Press key", "W+/BG" )
BoxSay( 1, 0, nTop, nLeft, nBottom, nRight, ;
"Arrow keys move the box", "W+/BG" )
BoxSay( 2, 0, nTop, nLeft, nBottom, nRight, ;
"Escape key exits program", "W+/BG" )
nRow := 3
DO WHILE nKey <> K_ESC
nKey := Inkey(0)
DO CASE
CASE nKey == K_UP
BoxUp( @nTop, @nLeft, @nBottom, @nRight, @cScreen )
CASE nKey == K_LEFT
BoxLeft( @nTop, @nLeft, @nBottom, @nRight, @cScreen )
CASE nKey == K_DOWN
BoxDown( @nTop, @nLeft, @nBottom, @nRight, @cScreen )
CASE nKey == K_RIGHT
BoxRight( @nTop, @nLeft, @nBottom, @nRight, @cScreen )
ENDCASE
BoxSay( nRow, nCol, nTop, nLeft, nBottom, nRight, ;
"The Inkey() Code was:" + LTrim(Str(nKey)), "W+/BG" )
nRow ++
nCol ++
IF Col() >= nRight-1
nCol := 0
ENDIF
ENDDO
RestScreen( nTop, nLeft, nBottom, nRight, cScreen )
RETURN
**********************************************************
* Box class programmed in a procedural style *
**********************************************************
//
// Display box with centered heading and
// return saved screen
//
#include "Setcurs.ch"
FUNCTION BoxShow( nTop, nLeft , nBottom, nRight, ;
cBox, cColor, cTitle )
LOCAL cScreen
cScreen := SaveScreen( nTop, nLeft, nBottom, nRight )
DispBox( nTop, nLeft, nBottom, nRight, cBox, cColor )
nRight := nLeft+ Round((nRight-nLeft-LEN(cTitle)+1)/2 ,0)
@ nTop, nRight SAY cTitle COLOR cColor
SetPos( nTop+1, nLeft+1 )
RETURN cScreen
//
// Show a value within the box by relative coordinates
// (0,0 corresponds to nTop+1 and nLeft+1). When the output line is
// past the bottom of the box, scroll the contents of the box
//
PROCEDURE BoxSay( nRow, nCol, nTop, nLeft, nBottom, nRight, ;
xValue, cColor )
LOCAL cOldColor := SetColor( cColor )
nRow += nTop +1
nCol += nLeft+1
IF nRow >= nBottom
nRow := nBottom-1
Scroll( nTop+1, nLeft+1, nBottom-1, nRight-1, 1 )
ENDIF
IF nCol >= nRight
nCol := nRight-1
ENDIF
@ nRow, nCol SAY Trim( PADR( xValue, nRight-nCol ))
SetColor( cOldColor )
RETURN
//
// Shift box one line up
//
PROCEDURE BoxUp( nTop, nLeft, nBottom, nRight, cBackGround )
LOCAL cForeGround
IF nTop > 0
DispBegin()
cForeGround := SaveScreen( nTop, nLeft, nBottom, nRight )
RestScreen( nTop, nLeft, nBottom, nRight, cBackGround )
nTop --
nBottom --
cBackGround := SaveScreen( nTop, nLeft, nBottom, nRight )
RestScreen( nTop, nLeft, nBottom, nRight, cForeGround )
SetPos( Row()-1, Col() )
DispEnd()
ENDIF
RETURN
//
// Shift box one column to the left
//
PROCEDURE BoxLeft( nTop, nLeft, nBottom, nRight, cBackGround )
LOCAL cForeGround
IF nLeft > 0
DispBegin()
cForeGround := SaveScreen( nTop, nLeft, nBottom, nRight )
RestScreen( nTop, nLeft, nBottom, nRight, cBackGround )
nLeft --
nRight --
cBackGround := SaveScreen( nTop, nLeft, nBottom, nRight )
RestScreen( nTop, nLeft, nBottom, nRight, cForeGround )
SetPos( Row(), Col()-1 )
DispEnd()
ENDIF
RETURN
//
// Shift box one line down
//
PROCEDURE BoxDown( nTop, nLeft, nBottom, nRight, cBackGround )
LOCAL cForeGround
IF nBottom < MaxRow()
DispBegin()
cForeGround := SaveScreen( nTop, nLeft, nBottom, nRight )
RestScreen( nTop, nLeft, nBottom, nRight, cBackGround )
nTop ++
nBottom ++
cBackGround := SaveScreen( nTop, nLeft, nBottom, nRight )
RestScreen( nTop, nLeft, nBottom, nRight, cForeGround )
SetPos( Row()+1, Col() )
DispEnd()
ENDIF
RETURN
//
// Shift box one column to the right
//
PROCEDURE BoxRight( nTop, nLeft, nBottom, nRight, cBackGround )
LOCAL cForeGround
IF nRight < MaxCol()
DispBegin()
cForeGround := SaveScreen( nTop, nLeft, nBottom, nRight )
RestScreen( nTop, nLeft, nBottom, nRight, cBackGround )
nLeft ++
nRight ++
cBackGround := SaveScreen( nTop, nLeft, nBottom, nRight )
RestScreen( nTop, nLeft, nBottom, nRight, cForeGround )
SetPos( Row(), Col()+1 )
DispEnd()
ENDIF
RETURN
If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.