⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 oop.htm

📁 strongForth: a strongly typed dialect of Forth implemented in ANS Forth.
💻 HTM
📖 第 1 页 / 共 5 页
字号:
in the example of the previous chapter. StrongForth's interpreter and compiler 
have no difficulty at all to always select the correct version, because the 
data type of the structure or the class of the object they are applied to is 
known at compile time. The same holds true for overloading data members and 
member words of different classes.</p>
<p>The three member words within the class definition are ordinary colon 
definitions. The fact that their last input parameter is always an object of the 
class makes them member words. To make the object available throughout the 
definitions, it is generally assigned to a local named <kbd>THIS</kbd>. Exactly 
like structures, objects can be allocated with <kbd>NEW</kbd> and deallocated with 
<kbd>DELETE</kbd>:</p>
<pre><u>NEW POINT CONSTANT P1</u>  OK
<u>P1 GET-POINT . .</u> 0 0  OK
<u>+5 -7 P1 SET-POINT</u>  OK
<u>P1 GET-POINT . .</u> -7 5  OK
<u>P1 DELETE</u>  OK</pre>
<p>Wait a moment. Why does the first <kbd>GET-POINT</kbd> return zero for both 
data members? They are supposed <em>not</em> to be automatically initialized! 
That's true. The initialization happens program controlled by implicitly executing 
the third member word <kbd>POINT</kbd>. <kbd>NEW</kbd> does not only allocate the 
data members of an object; it also evaluates a word with the same name as the 
class. This word is called a <em>constructor</em> of the class, and it usually 
contains code that initializes the data members. It is possible to provide multiple 
overloaded constructors with different sets of parameters, for example:</p>
<pre>: POINT ( SIGNED SIGNED POINT -- 3RD )
  LOCALS| THIS | THIS SET-POINT THIS ;</pre>
<p>Now, two different kinds of initializations are possible:</p>
<pre><u>NEW POINT DUP GET-POINT . . DELETE</u> 0 0  OK
<u>+12 +40 NEW POINT DUP GET-POINT . . DELETE</u> 40 12  OK</pre>
<p>A constructor always has exactly one output parameter, which is an unchanged 
copy of the object that is provided as the last input parameter. 
Note that <kbd>NEW</kbd> requires the existence of a constructor. 
Without a constructor, it is not possible to create an object of a class.</p>
<p>The size of an object in address units can be determined from its class 
with <kbd>SIZE-OBJECT</kbd>:<p>
<pre><u>DT POINT SIZE-OBJECT .</u> 12  OK</pre>
<p>12 address units? Since <kbd>POINT</kbd> has only two single cell members and one 
cell occupies 4 address units, shouldn't it rather be 8? Objects actually contain 
an additional cell for a pointer to the so-called <em>virtual member table</em>, 
which contains runtime type information of the object. This will be elaborated in detail 
in the section about virtual members. However, since the virtual member table also 
contains the size of objects of the class, it is possible to determine the object size 
not only from its class, but also from the object itself. This is not possible for 
structures, because structures do not have virtual member tables:</p>
<pre><u>NEW POINT DUP SIZE . DELETE</u> 2  OK</pre>
<p><kbd>SIZE</kbd> for items of data type <kbd>OBJECT</kbd> returns the size 
<em>in cells</em> of the object's data members. Based on <kbd>SIZE</kbd>, two words 
have been defined that can be applied to all objects:</p>
<pre>: COPY ( OBJECT 1ST -- )
  OVER SIZE OVER SIZE MIN
  ROT CAST ADDRESS -> SINGLE 1+ ROT CAST ADDRESS -> SINGLE 1+
  ROT MOVE ;

: ERASE ( OBJECT -- )
  DUP CAST ADDRESS -> SINGLE 1+ SWAP SIZE ERASE ;</pre>
<p><kbd>COPY</kbd> copies the data members of an object to the data members of another 
object of the same class. This is what is called in C++ 
<em>memberwise initialization</em>. However, note that something like
<em>default memberwise initialization</em> does not exist in StrongForth. All 
initialization has to be done explicitly. For specific objects, <kbd>COPY</kbd> 
can be overloaded if memberwise initialization is not desired. Since in most cases 
these overloaded versions require direct access to the data members, they usually 
have to be made member words.</p>
<p><kbd>ERASE</kbd> initializes all data members of an object with zero. Using 
<kbd>ERASE</kbd>, the first constructor of the <kbd>POINT</kbd> class can be 
simplified:</p>
<pre>: POINT ( POINT -- 1ST )
  DUP ERASE ;</pre>
<p>The definitions of <kbd>COPY</kbd> and <kbd>ERASE</kbd> reveal an implementation 
detail of objects. The phrase <kbd>CAST ADDRESS -> SINGLE 1+</kbd> shows that an 
item of data type <kbd>OBJECT</kbd> is nothing else but a pointer to an 
address one cell below the first data member. The cell it actually points to contains 
the pointer to the virtual member table:</p>
<table border=1 cellpadding=3 width=25%>
 <tr>
  <td align=center>virtual table pointer</td>
 </tr>
 <tr>
  <td align=center>PX</td>
 </tr>
 <tr>
  <td align=center>PY</td>
 </tr>
</table>
<h2><kbd>THIS</kbd> Object</h2>
<p>All data members and member words have a specific object of their class as the 
last input parameter. As the definition of the <kbd>POINT</kbd> class demonstrates, 
it is often convenient to have this object available as a local within the definition 
of a member word. By convention, this local is called <kbd>THIS</kbd>, and creating 
this local is usually the first action of member words. The phrase 
<kbd>LOCALS| THIS |</kbd> can be replaced by the immediate word <kbd>>THIS</kbd>, 
which does exactly the same thing. Since the ANS Forth host system does not generally 
permit multiple occurences of <kbd>LOCALS| ... |</kbd>, using 
<kbd>>THIS</kbd> and <kbd>LOCALS|</kbd> within the definition of the same member word 
might cause an exception being thrown. Instead of</p>
<pre>: COPY ( OBJECT 1ST -- )
  >THIS LOCALS| FROM |
  FROM CAST ADDRESS -> SINGLE 1+ 
  THIS CAST ADDRESS -> SINGLE 1+
  FROM SIZE THIS SIZE MIN MOVE ;</pre>
<p>you should rather write</p>
<pre>: COPY ( OBJECT 1ST -- )
  LOCALS| THIS FROM |
  FROM CAST ADDRESS -> SINGLE 1+ 
  THIS CAST ADDRESS -> SINGLE 1+
  FROM SIZE THIS SIZE MIN MOVE ;</pre>
<p>In the member word definitions of the <kbd>POINT</kbd> class, <kbd>THIS</kbd> 
is used quite often. Whenever a data member or another member word is used, it 
has to be preceeded by a reference to the <kbd>THIS</kbd> object. Wouldn't it be 
nice if the compiler automatically inserted <kbd>THIS</kbd> whenever a data member 
or a member word of the same class is used? Of course, it should only do so if an 
object of the class type is not already on the stack, because in some cases the 
data member or the member word belongs to a different object of the class type or 
even to an object of a different class. Such a feature really exists! It is actually 
the default for all data members defined with <kbd>MEMBER</kbd>, <kbd>CMEMBER</kbd>, 
etc. Member words that are compiled with <kbd>:MEMBER</kbd> instead of <kbd>:</kbd> 
also compile an implicit <kbd>THIS</kbd> if an object of the <kbd>THIS</kbd> class 
is not already on the data stack. The definition of the <kbd>POINT</kbd> class can 
now be written shorter:</p>
<pre>DT OBJECT PROCREATES POINT

CLASS POINT
BODY
  +0 MEMBER PX
  +0 MEMBER PY
  :MEMBER SET-POINT ( SIGNED SIGNED POINT -- )
    >THIS PY ! PX ! ;
  :MEMBER GET-POINT ( POINT -- SIGNED SIGNED )
    >THIS PX @ PY @ ;
  :MEMBER POINT ( POINT -- 1ST )
    >THIS +0 +0 SET-POINT THIS ;
ENDCLASS</pre>
<p>The <em>Automatic <kbd>THIS</kbd></em> feature works only within class definitions, 
because that's where almost all usages of data members and own member words refer to 
<kbd>THIS</kbd>. It is important to understand how this feature works, because 
it can lead to ambiguities if not used with care. Let's investigate what happens once 
the compiler parses <kbd>PY</kbd> in the definition of <kbd>SET-POINT</kbd>. At this 
point, the compiler data type heap consists of two times data type <kbd>SIGNED</kbd>. 
The attempt to find <kbd>PY</kbd> in the dictionary fails, because <kbd>PY</kbd> 
has an object of data type <kbd>POINT</kbd> as its only input parameter. But the 
search continues. <kbd>CLASS</kbd> actually appends a special word list called 
<kbd>AUTOTHIS</kbd> to the <em>end</em> of the search order. This word list is 
only searched when the search in all other word lists of the search order failed. 
<kbd>AUTOTHIS</kbd> contains an immediate word <kbd>PY</kbd> with no parameters, 
that was created by the previous definition of <kbd>PY</kbd> with <kbd>MEMBER</kbd>. 
This immediate word temporarily removes the <kbd>AUTOTHIS</kbd> word list from the 
search order, then evaluates <kbd>THIS PY</kbd>, and finally restores the search 
order. Temporarily removing the <kbd>AUTOTHIS</kbd> word list from the search 
order is necessary in order to avoid recursive executions of the immediate word if 
inserting <kbd>THIS</kbd> doesn't help. But in this case, evaluating <kbd>THIS</kbd> 
results in <kbd>SIGNED SIGNED POINT</kbd> on the compiler data type heap, and 
the subsequent search of <kbd>PY</kbd> is successful. The same thing happens with 
<kbd>PX</kbd> in the definition of <kbd>SET-POINT</kbd>.</p>
<p><kbd>:MEMBER</kbd> and all defining words for data members actually create two 
definitions: the proper definition in the current compilation word list, and an 
immediate word with the same name in the <kbd>AUTOTHIS</kbd> word list. Note that 
the <em>Automatic <kbd>THIS</kbd></em> feature works only after a local with the name 
<kbd>THIS</kbd> has been defined.</p>
<p>The defining word that defines the immediate word is called <kbd>AUTOTHIS</kbd>, 
just like the word list.
It parses the input source for the name of the word to be defined, and then skips back 
in the input source specification, so that the name can be parsed once more. With 
<kbd>AUTOTHIS</kbd>, The definition of <kbd>:MEMBER</kbd> becomes pretty simple:</p>
<pre>: :MEMBER ( OBJ-SIZE -- 1ST COLON-DEFINITION )
  AUTOTHIS : ;</pre>
<p>The two words that add and remove the <kbd>AUTOTHIS</kbd> word list to and from the 
search order might be useful at other places, because they work with other word lists 
as well:</p>
<pre><u>WORDS APPEND-WORDLIST</u>
APPEND-WORDLIST ( WID -- )  OK
<u>WORDS STRIP-WORDLIST</u>
STRIP-WORDLIST ( -- )  OK
<u>ORDER</u>
CURRENT: FORTH
CONTEXT: FORTH  OK
<u>ENVIRONMENT-WORDLIST APPEND-WORDLIST ORDER</u>
CURRENT: FORTH
CONTEXT: FORTH ENVIRONMENT  OK
<u>STRIP-WORDLIST ORDER</u>
CURRENT: FORTH
CONTEXT: FORTH  OK</pre>
<p>Note that the <em>Automatic <kbd>THIS</kbd></em> feature does not work with 
<kbd>RECURSE</kbd>. The reason is that <kbd>RECURSE</kbd> does not actually perform 
a dictionary search. It just tries to compile the current definition and expects 
that its parameters are on the stack. Recursive member words generally require an 
explicit <kbd>THIS</kbd> before the <kbd>RECURSE</kbd>.</p>
<h2>Encapsulation</h2>
<p>Encapsulation is one of the major properties of object oriented programming. It 
means that classes hide the details of their internal data representation by just 
providing access to a number of interface member words that restrict the access to 
internal data. The internal representation of the <kbd>POINT</kbd> class is not 
encapsulated, because you can freely access it's data members:</p>
<pre><u>NEW POINT CONSTANT POINT3</u>  OK
<u>-20 POINT3 PX !</u>  OK
<u>POINT3 GET-POINT . .</u> 0 -20  OK</pre>
<p>Like C++, StrongForth has three levels of information hiding:</p>
<ul>
 <li><em>Private</em> members can be accessed only within the same class definition.
 For example, if <kbd>PX</kbd> were a private data member, it could be used by 
 <kbd>SET-POINT</kbd>, <kbd>GET-POINT</kbd> and <kbd>POINT</kbd>, but not by any 
 word that is defined after <kbd>ENDCLASS</kbd>.</li>
 <li><em>Protected</em> members can be accessed like private members, and 
 additionally within the class definitions of all derived classes.</li>
 <li><em>Public</em> members don't have any access restrictions. So far, all members 
 of the <kbd>POINT</kbd> class are public.</li>
</ul>
<p>Note that the three levels can be applied to both data members and member words. 
Usually, all data members are either private or protected, but it is often useful 
also to restict access to member words that are supposed to be used only internally 
to the class.</p>
<p>Access to data members and member words can be restricted by defining them in 
the <kbd>PRIVATE</kbd> or <kbd>PROTECTED</kbd> word lists. 
Each class has its own instances of these two word 
lists, and it is generally not possible to access them from outside the class 
definition. For example, we can make the data members of the <kbd>POINT</kbd> class 
private:</p>
<pre>DT OBJECT PROCREATES POINT

CLASS POINT
BODY
  ALSO PRIVATE DEFINITIONS
  +0 MEMBER PX
  +0 MEMBER PY
  ALSO FORTH DEFINITIONS PREVIOUS
  :MEMBER SET-POINT ( SIGNED SIGNED POINT -- )
    >THIS PY ! PX ! ;
  :MEMBER GET-POINT ( POINT -- SIGNED SIGNED )
    >THIS PX @ PY @ ;
  :MEMBER POINT ( POINT -- 1ST )
    >THIS +0 +0 SET-POINT THIS ;
ENDCLASS</pre>
<p>To be able to access the two data members within the class definition, the 
<kbd>PRIVATE</kbd> word list has to be in the search order. <kbd>CLASS</kbd> 
saves the search order and the current compilation word list at the beginning 
of the class definition, and <kbd>ENDCLASS</kbd> restores both. 
After <kbd>ENDCLASS</kbd>, the <kbd>PRIVATE</kbd> word list of the 
<kbd>POINT</kbd> class is inaccessible, which means that all access to the 
data members is restricted to using the public member words:</p>
<pre><u>NEW POINT CONSTANT POINT4</u>  OK
<u>+20 POINT4 PX !</u>
+20 POINT4 PX ? undefined word
SIGNED POINT
<u>POINT4 GET-POINT . .</u> 0 0  OK
<u>+20 +0 POINT4 SET-POINT</u>  OK
<u>POINT4 GET-POINT . .</u> 0 20  OK</pre>
<p>In some cases, it is necessary for one class to access the data members of 
another class. Does this mean you have to make the data members public? No, 
not necessarily. StrongForth supports the same mechanism as C++. A class that 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -