📄 oop.htm
字号:
<html>
<head>
<title>Object Oriented Programming</title>
</head>
<body>
<h1>Object Oriented Programming</h1>
<p>A library for data structures and object oriented programming is available for
StrongForth.f. It is mainly based on the C++ model
of object oriented programming, providing similar features and using the same
terminology. It fully supports polymorphism, encapsulation and inheritance. Objects
are actually special data types that can be passed on the stack and that are
consumed by member functions. Other important features and properties are:</p>
<ul>
<li>early and late binding,</li>
<li>single inheritance for code and data,</li>
<li>static, dynamic and class specific memory allocation,</li>
<li>explicit constructors, and</li>
<li>full support for bit fields.</li>
</ul>
<p>Multiple class inheritance is not supported. Furthermore, all kinds of
implicit data type conversions and default actions have been omitted. Implicit data
type conversions are dangerous anyway, because they are somtimes ambiguous and
can lead to unexpected results. Anyway, StrongForth's hierarchical data type
system often makes type conversions obsolete, because words that expect items of
a specific data type can generally be applied to all subtypes of it.</p>
<p>Object oriented programming can be made available by first including the
<em>Memory-Allocation</em> word set, and then including the object oriented
programming library:</p>
<pre>" memory.sf" INCLUDE
" OOP.sf" INCLUDE
</pre>
<p>Data structures are classes without dedicated member functions. They are actually
simplified kinds of classes. That's why we'll start with data structures as an
introduction to the object oriented programming library.</p>
<h2>Structures</h2>
<p>Data structures are a pretty common feature of high-level programming languages.
A data structure consists of a set of data members that can be accessed in predefined
ways. For example, consider a data structure that describes a rectangle on the
screen. A very simple version might consist of its width and its height, plus the
coordinates of its lower left corner.</p>
<p>There are numerous ways to implement data structures
in ANS Forth. In StrongForth, the data type system makes the implementation a little
bit more challenging, because each structure and each of its members has to have a
data type. As a reward, StrongForth's structures are type-save. You can only store data
of the specified type in a data member, and you cannot access members that do not
exist in a structure, unless you use an explicit type cast. Furthermore, there are no
name conflicts between members of different structures. You can overload members as
often as you like without any risk of accessing a member of the wrong structure.</p>
<p>Here's a first example of a simple structure definition and how it can be used:</p>
<pre>DT STRUCTURE PROCREATES RECTANGLE
STRUCT RECTANGLE
NULL SIGNED MEMBER PX
NULL SIGNED MEMBER PY
NULL UNSIGNED MEMBER WIDTH
NULL UNSIGNED MEMBER HEIGHT
ENDSTRUCT
NEW RECTANGLE CONSTANT RECT1
+100 RECT1 PX !
+150 RECT1 PY !
40 RECT1 WIDTH !
25 RECT1 HEIGHT !</pre>
<p>Data type <kbd>STRUCTURE</kbd> is a direct subtype of data type <kbd>SINGLE</kbd>.
Each new type of structure has its own data type that has to be directly or indirectly
derived from <kbd>STRUCTURE</kbd>. An extended version of <kbd>PROCREATES</kbd>
reserves memory space for specific attributes of data types that are derived from
<kbd>STRUCTURE</kbd>. The fact that each kind of structure is a unique data type
enables the interpreter and the compiler to perform the necessary type checks and to
ensure that only data members that are actually included in a given structure can be
accessed.</p>
<p>Once a new data type has been created, the data members can be defined between
<kbd>STRUCT</kbd> and <kbd>ENDSTRUCT</kbd>. <kbd>STRUCT</kbd> checks whether the
given data type was really derived from data type <kbd>STRUCTURE</kbd> and leaves
an item of data type <kbd>STRUCT-SIZE</kbd> on the stack, which counts the
size of the structure bits. It is incremented each time a new
data member is added to the structure. At the end of the structure definition,
<kbd>ENDSTRUCT</kbd> stores the accumulated size of the data structure as an
attribute of the structure's data type. The data type of the currently defined
structure (<kbd>RECTANGLE</kbd>) is stored in the global variable
<kbd>THIS-CLASS</kbd> to be readily available whenever it's needed.</p>
<p>The creation of a new data type for the structure is decoupled from the structure
definition. I. e., you first have to create a new data type that is directly or
indirectly derived from data type <kbd>STRUCTURE</kbd>, and then you can specify
the members of the structure. This decoupling makes it possible to define members
of the structure data type itself, for example if a structure shall contain a
pointer to another structure of the same type. Furthermore, cross references
between two structures can be implemented easily, like in this example:</p>
<pre>DT STRUCTURE PROCREATES STRUCT-A
DT STRUCTURE PROCREATES STRUCT-B
STRUCT STRUCT-A
NULL STRUCT-B MEMBER SB
\ ... \
ENDSTRUCT
STRUCT STRUCT-B
NULL STRUCT-A MEMBER SA
\ ... \
ENDSTRUCT</pre>
<p><kbd>MEMBER</kbd> is a defining word that defines the data members of the
structure. In the above example, the data members are <kbd>PX</kbd>, <kbd>PY</kbd>,
<kbd>WIDTH</kbd> and <kbd>HEIGHT</kbd>:</p>
<pre>PX ( RECTANGLE -- ADDRESS -> SIGNED )
PY ( RECTANGLE -- ADDRESS -> SIGNED )
WIDTH ( RECTANGLE -- ADDRESS -> UNSIGNED )
HEIGHT ( RECTANGLE -- ADDRESS -> UNSIGNED )</pre>
<p><kbd>MEMBER</kbd> expects the current size
of the structure in bits, which is originally supplied by <kbd>STRUCT</kbd>, plus
a dummy parameter that has the data type of the new member. A member definition
looks like an ordinary variable definition. But since the data member is not being
initialized, a null item can be provided as a sample for the data type. The
execution semantic of a data member is to return its address within a specific
instance of the structure. StrongForth does not provide defining words for data
members that behave like values.</p>
<p>Structure <kbd>RECT1</kbd> of the example may thus be used as follows:</p>
<pre><u>40 RECT1 .S</u> UNSIGNED RECTANGLE OK
<u>WIDTH .S !</u> UNSIGNED ADDRESS -> UNSIGNED OK
<u>25 RECT1 HEIGHT !</u> OK
<u>RECT1 WIDTH @ .</u> 40 OK</pre>
<p><kbd>NEW</kbd> allocates dynamic memory for a structure of a given type. The
new instance of the structure may be stored as a constant, as in this example.
<kbd>PX</kbd>, <kbd>PY</kbd>, <kbd>WIDTH</kbd> and <kbd>HEIGHT</kbd> can then be
used to access the data members of the structure.</p>
<p>Of course, data members need not be all single-cell items. It is also
possible to define double-cell items and character size
items as members, or even arrays of items with the same data type. Here's a
more comprehensive example:</p>
<pre><u>DT STRUCTURE PROCREATES HEADER</u> OK</u>
<u>STRUCT HEADER</u> OK
<u> NULL UNSIGNED CMEMBER WLENGTH</u> OK
<u> NULL CHARACTER 31 CMEMBERS WNAME ALIGNED</u> OK
<u> NULL ADDRESS MEMBER WLINK</u> OK
<u> NULL LOGICAL MEMBER ATTRIBUTES</u> OK
<u> NULL TOKEN MEMBER CODEFIELD</u> OK
<u> NULL DATA-TYPE 8 MEMBERS PARAMETERS</u> OK
<u>ENDSTRUCT</u> OK
<u>NEW HEADER CONSTANT WORD1</u> OK
<u>" TEST" DUP WORD1 WLENGTH .S !</u> CADDRESS -> CHARACTER UNSIGNED UNSIGNED CADDRESS -> UNSIGNED OK
<u>WORD1 WNAME SWAP .S MOVE</u> CADDRESS -> CHARACTER CADDRESS -> CHARACTER UNSIGNED OK
<u>DICT-HERE WORD1 WLINK .S !</u> ADDRESS ADDRESS -> ADDRESS OK
<u>'HOST 2/ WORD1 CODEFIELD .S !</u> TOKEN ADDRESS -> TOKEN OK
<u>2 CAST LOGICAL WORD1 ATTRIBUTES .S !</u> LOGICAL ADDRESS -> LOGICAL OK
<u>DT SIGNED DT-INPUT OR WORD1 PARAMETERS .S !</u> DATA-TYPE ADDRESS -> DATA-TYPE OK
<u>DT SIGNED DT-OUTPUT OR 1 OFFSET+ WORD1 PARAMETERS 1+ .S !</u> DATA-TYPE ADDRESS -> DATA-TYPE OK
<u>WORD1 WNAME WORD1 WLENGTH @ .S</u> CADDRESS -> CHARACTER UNSIGNED OK
<u>TYPE</u> TEST OK</pre>
<p><kbd>CMEMBER</kbd> defines a character size data member, whose address is of
data type <kbd>CADDRESS -> ...</kbd>. Of course, defining character size members
can lead to the following members becoming unaligned. <kbd>ALIGNED</kbd> is used
after the second character size member to re-align the offset. Since the already
existing version of <kbd>ALIGNED</kbd> only works for addresses, an overloaded
version for items of data type <kbd>STRUCT-SIZE</kbd> has to be provided:</p>
<pre>ALIGNED ( STRUCT-SIZE -- 1ST )</pre>
<p>Arrays of data members can easily be defined with <kbd>MEMBERS</kbd>,
<kbd>CMEMBERS</kbd> etc. These defining words expect an additional size parameter
on the stack. Their runtime semantics is to return the address of the first
array element. Here's a list of all defining words for data members:</p>
<pre>MEMBER ( STRUCT-SIZE SINGLE -- 1ST )
MEMBER ( STRUCT-SIZE DOUBLE -- 1ST )
CMEMBER ( STRUCT-SIZE SINGLE -- 1ST )
MEMBERS ( STRUCT-SIZE SINGLE UNSIGNED -- 1ST )
MEMBERS ( STRUCT-SIZE DOUBLE UNSIGNED -- 1ST )
CMEMBERS ( STRUCT-SIZE SINGLE UNSIGNED -- 1ST )</pre>
<p>In connection with structures and classes, overloading becomes once
more a very useful feature, because the names of data members can be reused for different
structures and classes. Since the input parameter of member definitions like
<kbd>PX</kbd> has the data type of the structure it belongs to, data members from
different structures do never interfere, even if they have the same name. The
interpreter and the compiler are always able to chose the correct version.</p>
<p>New instances of structures can be dynamically allocated with <kbd>NEW</kbd>.
To free the dynamic memory space occupied by a structure, you should use
<kbd>DELETE</kbd>:</p>
<pre><u>RECT1 DELETE</u> OK</pre>
<p>If you prefer to allocate static memory space or to use any other place for the
structure's data members, you simply provide <kbd>NEW</kbd> with the address as
an additional parameter of data type <kbd>ADDRESS</kbd> or <kbd>CADDRESS</kbd>:</p>
<pre><u>HERE DT RECTANGLE SIZE-STRUCTURE ALLOT .S</u> ADDRESS OK
<u>NEW RECTANGLE CONSTANT RECT2</u> OK</pre>
<p><kbd>SIZE-STRUCTURE</kbd> determines the size of a structure in address units.
Just note that <kbd>SIZE-STRUCTURE</kbd> expects the data type of a structure and
not one of its instances. Of course, structures that are not allocated in the
dynamic memory space may not be deleted. Applying <kbd>DELETE</kbd> to a statically
allocated structure will cause an ambiguous condition and can lead to a system
crash.</p>
<p>Usually structures are direct children of data type <kbd>STRUCTURE</kbd>. But
since in StrongForth structures are just stripped-down classes, it is not
prohibited to derive a structure from a child or grandchild of <kbd>STRUCTURE</kbd>.
What happens if you do that? Well, the new structure inherits the data members from
its parent and allows adding additional members. The new structure is just an
extension of the old one:</p>
<pre><u>DT RECTANGLE SIZE-STRUCTURE .</u> 16 OK
<u>DT RECTANGLE PROCREATES SCREEN-RECTANGLE</u> OK
<u>STRUCT SCREEN-RECTANGLE</u> OK
<u> NULL FLAG MEMBER VISIBLE</u> OK
<u>ENDSTRUCT</u> OK
<u>DT SCREEN-RECTANGLE SIZE-STRUCTURE .</u> 20 OK
<u>NEW SCREEN-RECTANGLE CONSTANT SRECT</u> OK
<u>SRECT PX .</u> 1525632 OK
<u>SRECT PY .</u> 1525636 OK
<u>SRECT WIDTH .</u> 1525640 OK
<u>SRECT HEIGHT .</u> 1525644 OK
<u>SRECT VISIBLE .</u> 1525648 OK
<u>RECT2 VISIBLE .</u>
RECT2 VISIBLE ? undefined word
RECTANGLE</pre>
<p><kbd>VISIBLE</kbd> expects an item of data type <kbd>SCREEN-RECTANGLE</kbd>
on the stack. Since <kbd>SCREEN-RECTANGLE</kbd> is a child of <kbd>RECTANGLE</kbd>,
<kbd>VISIBLE</kbd> cannot be applied to a <kbd>RECTANGLE</kbd>, but <kbd>PX</kbd>,
<kbd>PY</kbd>, <kbd>WIDTH</kbd> and <kbd>HEIGHT</kbd> can be applied to a
<kbd>SCREEN-RECTANGLE</kbd>.</p>
<p>Note that a parent structure always has to be defined before its children.
Otherwise, the definitions of a child structure couldn't determine the members
it inherits from its parent. Of course, this restriction applies to classes as
well. Note also that it is possible to redefine a structure (and a class). If an
exception is thrown during a structure definition you may thus just give it a
second try without the necessity to create a new data type for the structure.</p>
<h2>Classes</h2>
<p>The definition of a class looks quite similar to the definition of a structure.
Classes are data types that are directly or indirectly derived from data type
<kbd>OBJECT</kbd>, which is in turn a child of <kbd>SINGLE</kbd>. The class
definition is enclosed between the words <kbd>CLASS</kbd> and <kbd>ENDCLASS</kbd>.
Additionally, the word <kbd>BODY</kbd> divides the class definition in two parts
The first part, between <kbd>CLASS</kbd> and <kbd>BODY</kbd>, will be described
later in this chapter. Here's a first example that only uses the second
part:</p>
<pre>DT OBJECT PROCREATES POINT
CLASS POINT
\ first part is left empty
BODY
\ second part starts here
+0 MEMBER PX
+0 MEMBER PY
: SET-POINT ( SIGNED SIGNED POINT -- )
LOCALS| THIS | THIS PY ! THIS PX ! ;
: GET-POINT ( POINT -- SIGNED SIGNED )
LOCALS| THIS | THIS PX @ THIS PY @ ;
: POINT ( POINT -- 1ST )
LOCALS| THIS | +0 +0 THIS SET-POINT THIS ;
ENDCLASS</pre>
<p>The class definition of <kbd>POINT</kbd> contains two data members <kbd>PX</kbd>
and <kbd>PY</kbd> of data type <kbd>SIGNED</kbd>, plus three member words. The
defining words for data members are the same as those you already know from
structure definitions. Just as with structures, the data members are not
automatically initialized. Note that <kbd>PX</kbd> and <kbd>PY</kbd> are
overloaded versions of two of the data members of structure <kbd>RECTANGLE</kbd>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -