home *** CD-ROM | disk | FTP | other *** search
/ Solo Programadores 22 / SOLO_22.iso / docs / lovelace / lesson12.les < prev    next >
Encoding:
Text File  |  1995-12-07  |  13.4 KB  |  345 lines

  1. <COMMENT This is a lesson file for the Lovelace Ada tutorial>
  2. <COMMENT A program called genlesson is used to transform this file into a set>
  3. <COMMENT of useful HTML files for use by Mosaic & other WWW browsers.>
  4.  
  5. <COMMENT  Edit the following lines. >
  6. <TUTOR NAME="Lovelace">
  7. <LESSON NUMBER=12>
  8. <AUTHOR NAME="David A. Wheeler" EMAIL="wheeler@ida.org">
  9. <AUTHOR ADDRESS="<A HREF="dwheeler.htm">David A. Wheeler (wheeler@ida.org)</A>">
  10. <COMMENT $Id: lesson12.les,v 1.2 1995/04/28 20:32:53 wheeler Exp $ >
  11.  
  12. <COMMENT  You'll probably want to uncomment and edit these lines: >
  13. <COMMENT  <PREVIOUS_LESSON LOCATION="URL_of_directory/" >
  14. <COMMENT  <NEXT_LESSON LOCATION="URL_of_directory/" >
  15.  
  16. <COMMENT A lesson is divided into 1 or more "sections".>
  17. <COMMENT Each section has a title; SECTION starts a new section.>
  18.  
  19. <SECTION NAME="Basics of Access Variables">
  20. It is often very useful to have a variable that, instead of storing
  21. a value, stores a reference to some other object or a subprogram.
  22. Such variables are called <EM>access</EM> variables in Ada, and
  23. are essentially equivalent to pointers or references in other languages.
  24. One common use of access variables is to implement items of varying size.
  25. <P>
  26. To create such variables, first create a type for it, called an
  27. access type.
  28. <P>
  29. Here's an example of an access type declaration,
  30. declaring that variables of type Node_Access
  31. can access (reference) objects of type Node:
  32. <P>
  33. <PRE>
  34.   type Node_Access is access Node;
  35. </PRE>
  36. <P>
  37. Here is the
  38. <A HREF="bnf.htm">BNF</A> for creating an access type to an object:
  39. <P>
  40. <PRE>
  41.   access_object_type_declaration ::= "type" new_type_name "is"
  42.                                     "access" ["all"] type_name ";"
  43. </PRE>
  44. <P>
  45. Variables of access type can either refer to an object or be
  46. <EM>null</EM>.
  47. You can make an access variable null by assigning it the value of the
  48. keyword "null", and you can check if it's null by comparing the variable
  49. (using "=" or "/=") to "null".
  50. <P>
  51. The ability to "point" at other values is useful and efficient, but it
  52. can also be dangerous.
  53. It's easy to do the wrong thing with pointers and cause surprising results.
  54. Ada tries to limit the damage that you can do while maintaining efficiency.
  55. Ada does this through the following rules:
  56. <UL>
  57. <LI>
  58. All access type variables are initialized as null (unless you specifically
  59. initialize them to something else).
  60. <LI>
  61. All operations that use what an access value references first check
  62. to see if the access value is null.
  63. If the access value is null, the exception Constraint_Error is raised.
  64. <LI>
  65. Normally access-to-object types are limited to referring to
  66. objects ``created dynamically'', as we will discuss next.
  67. You must add the keyword ``all'' to permit an object type to refer
  68. to all objects of a given type; such access values are called
  69. <EM>general</EM> access objects and are new to Ada 95.
  70. General access objects are a little dangerous than regular access types,
  71. so it's best to avoid using them where practical.
  72. One use for general access objects is to interface with C or C++ programs,
  73. since C and C++ pointers are equivalent to Ada general access objects.
  74. Another important use for general access objects is for object-oriented
  75. programming, as we'll discuss later.
  76. </UL>
  77. <P>
  78. The Ada compiler will optimize these checks and initializations away
  79. if it can determine that they're unnecessary.
  80. You can also turn off these checks for a given subprogram if you know
  81. that that particular subprogram is totally correct.
  82. <P>
  83. Now that we know how to declare access types, let's see how we can use them.
  84.  
  85. <SECTION NAME="Simple Uses of Access Variables">
  86. Let's imagine that we want to create a binary tree of Unbounded_Strings.
  87. A binary tree is a set of nodes; each node has a way to locate its
  88. parent, its left child node, and a right child node.
  89. Each node also has data it contains (an Unbounded_String).
  90. Defining a record for a binary tree node using access values is easy:
  91. <P>
  92. <PRE>
  93.   type Tree_Node is
  94.     record
  95.       Parent      : Tree_Access;
  96.       Left, Right : Tree_Access;
  97.       Data        : Unbounded_String;
  98.     end record;
  99. </PRE>
  100. <P>
  101. Of course, defining Tree_Node depends on
  102. a type called Tree_Access, which depends on a type called Tree_Node.
  103. Note the circularity - each type's definition depends on the other.
  104. The way to solve this is to use an ``incomplete type declaration''
  105. of the record (this is the same thing you'd do in C or Pascal).
  106. An incomplete type declaration has the keyword "type", the name of the
  107. type that you plan to declare later, and an immediately following semicolon.
  108. For example, before defining Tree_Node you'd do the following:
  109. <P>
  110. <PRE>
  111.   type Tree_Node; -- Incomplete type declaration.
  112.   type Tree_Access is access Tree_Node;
  113. </PRE>
  114. <P>
  115. You can then declare variables of access types using the normal
  116. variable declaration syntax, for example, you can create two
  117. access variables this way:
  118. <P>
  119. <PRE>
  120.   Current, Root : Tree_Access;
  121. </PRE>
  122. <P>
  123. What we need is an operation that will create a new Tree_Node object
  124. (in a general storage area) and then return an
  125. access value referencing this newly-created object.
  126. The process of creating a new object this way is called <EM>allocation</EM>
  127. In C the allocation operation is called "malloc", while in Pascal and
  128. C++ it's called "new".
  129. Ada also calls this operation <EM>new</EM>, and Ada has the same
  130. syntax as C++ and Pascal;
  131. the keyword new is followed by the name of the type to be created.
  132. For example, here's how to create a new object of type
  133. Tree_Node, setting the value of "Current" to access it:
  134. <P>
  135. <PRE>
  136.   Current := new Tree_Node;
  137. </PRE>
  138. <P>
  139. Once an access value references a real object (instead of being null), you
  140. can use the ``dot'' operation to refer to the object pointed to by the access
  141. value.
  142. This is the same syntax as when accessing values in a record.
  143. For example, here's how to set the data value of the Node being pointed to
  144. by Current:
  145. <P>
  146. <PRE>
  147.   Current.Data := To_Unbounded_String("Hello!");
  148. </PRE>
  149. <P>
  150. In some cases you want to work with the "entire" object being
  151. accessed instead of a piece of it. For example, let's say that you
  152. have some procedure (My_Procedure) that requires as input a Tree_Node.
  153. You can't just pass in an access value to a Tree_Node, because the
  154. types are different (an access value is different than what it accesses).
  155. To handle this, simply use the word "all" after the dot operation
  156. to refer to the entire object:
  157. <P>
  158. <PRE>
  159.   My_Procedure(Current.all);
  160. </PRE>
  161. <P>
  162. <P>
  163. Finally, you can have multiple different pointers referring to the same object.
  164. This is called access assignment; all you need to do is use ordinary
  165. assignment statements.
  166. For example, to set the access value "Root" so that it references the
  167. same node as "Current", simply do:
  168. <P>
  169. <PRE>
  170.   Root := Current;
  171. </PRE>
  172. <P>
  173.  
  174. <QUESTION Type=Multiple-Choice>
  175. If you execute the statements listed above, what is the value of Root.Data?
  176. <CHOICES>
  177. <CHOICE ANS=1>An empty string.
  178. <CHOICE ANS=2>"Hello!"
  179. <CHOICE ANS=3>None; an exception would be raised.
  180. </CHOICES>
  181. <ANSWER ANS=2>
  182. <RESPONSES>
  183. <WHEN ANS=1>
  184. No, sorry.
  185. <WHEN ANS=2>
  186. Quite right.
  187. Root now references the same object as "Current" does, and the
  188. Tree_Node that both of them reference has "Hello!" as its value in Data.
  189. <WHEN ANS=3>
  190. Sorry.
  191. If we hadn't set "Root := Current" an exception would have been
  192. raised, but now "Root" references the same object as "Current" does.
  193. Please try again.
  194. </RESPONSES>
  195.  
  196. <SECTION NAME="Access Parameters (For Object Orientation)">
  197.  
  198. Often when developing object-oriented systems you will want to 
  199. pass around access values to tagged types.
  200. Ada 95 adds a new pseudo-mode called "access" to help you build OO
  201. systems using access types.
  202. <P>
  203. If you recall, every parameter for a subprogram has to be of mode
  204. <I>in</I>, <I>in out</I>, or <I>out</I>.
  205. You can use the keyword "access" as a mode (followed by a type name) instead.
  206. Here's an example:
  207. <P>
  208. <PRE>
  209.  procedure Get(Agent : access Occupant; Direct_Object : access Occupant'Class);
  210. </PRE>
  211. <P>
  212. So what in the world does this example mean??
  213. Here's the answer:
  214. <UL>
  215. <LI>
  216. When an "access mode" is followed by an ordinary tagged type,
  217. it means that the input parameter (in this case Agent) must be
  218. an access value of the given type (in this case Occupant).
  219. More importantly, this procedure can be overridden, and the
  220. type of the <I>object being accessed</I> will determine which subprogram
  221. to dispatch to.
  222. Thus, we could create another subprogram called "Get" for a descendent
  223. of Occupant, and that subprogram Get would override the Get defined here.
  224. This is an essential part of being an OO language - we can dispatch to
  225. a given program using the current data value.
  226. Access parameters let us dispatch using the access value.
  227. <LI>
  228. When an "access mode" is followed by an class type,
  229. it means that the input parameter (in this case Direct_Object) must be
  230. an access value of the given type (in this case Occupant) or <I>any</I>
  231. of its descendents.
  232. In this case we do <I>not</I> dispatch on this parameter,
  233. since any of the descendents will do for this subprogram.
  234. In this case, access parameters let us accept a wide range of types,
  235. instead of just a specific access type.
  236. </UL>
  237. <P>
  238. There's an important requirement for access parameters - null values are
  239. not permitted.
  240. If you want to permit null values, use the modes in, out, or in out with
  241. an ordinary access type.
  242. <P>
  243. It's difficult to understand access parameters
  244. without more context, so we'll defer discussing this further until
  245. <A HREF="lesson18.htm">lesson 18</A> where we will
  246. look at examples of this.
  247. What you need to understand right now is that if you're using access types
  248. and object-oriented programming, you will probably want to use
  249. the pseudomode "access".
  250.  
  251. <SECTION NAME="Unchecked_Deallocation">
  252. Over time it's possible that some objects will no longer
  253. be referenced.
  254. Handling this case is called ``garbage collection.''
  255. Ada was designed to permit, but not require, automatic garbage collection.
  256. Since most Ada compilers do not perform automatic garbage collection,
  257. it's more portable to assume that garbage collection won't be performed
  258. automatically.
  259. Note that all Ada compilers that generate
  260. <A HREF="http://www.yahoo.com/Computers/Languages/Java">Java</A> J-code
  261. (bytecodes)
  262. <EM>do</EM> perform automatic garbage collection.
  263. <P>
  264. Ada provides a generic procedure to release an object if it's no
  265. longer being referenced.
  266. This procedure is equivalent to ``free'' in C and ``delete'' in C++.
  267. This generic procedure's name is ``Unchecked_Deallocation''.
  268. Since it's a generic, you need to instantiate the generic using the
  269. access type you're using.
  270. By convention, the name of the instantiation is usually called "Free".
  271. <P>
  272. Here's the official definition of generic procedure
  273. Unchecked_Deallocation:
  274. <P>
  275. <PRE>
  276.   generic
  277.     type Object(<>) is limited private;
  278.     type Name   is access  Object;
  279.   procedure Unchecked_Deallocation(X : in out Name);
  280. </PRE>
  281. <P>
  282. Note that we need to pass it two things; a type, and the access to that type.
  283. Here's a simple example - let's instantiate a procedure called ``Free''
  284. that will let us release objects when they're no longer used:
  285. <P>
  286. <PRE>
  287.   procedure Free is new Unchecked_Deallocation(Tree_Node, Tree_Access);
  288. </PRE>
  289. <P>
  290. Now that we've instantiated a procedure called ``Free'', we can call it.
  291. Let's continue our example; imagine that we don't want to use
  292. the node we created in the last section any more.
  293. That's fine, we'll just call the new ``Free'' routine we've created:
  294. <P>
  295. <PRE>
  296.   Free(Current);
  297. </PRE>
  298. <P>
  299. When Free returns, the variable Current will have the value "null", and
  300. the memory that used to be used by the Tree_Node will have been released.
  301. Any instantiation of Unchecked_Deallocation will automatically call
  302. any finalization operations defined for the enclosed types, as you would expect.
  303. <P>
  304. An important problem arises here that also arises in C, C++, and Pascal:
  305. what if there's another access type that refers to that object?
  306. In our example, the access variable ``Root'' still refers to an object,
  307. but that object no longer exists.
  308. Any attempt to use Root to access its object may result in unpredictable
  309. behavior.
  310. While Ada provides a number of protections in the use of access variables,
  311. this is one problem Ada doesn't completely protect against.
  312. <P>
  313. This is one area where there is a strong tension between the desire
  314. to be safe and easy to use versus the desire to be predictably efficient.
  315. Some languages require deallocation to be handled automatically;
  316. this is called <EM>automatic garbage collection</EM>.
  317. Examples of such languages include Smalltalk, Eiffel, and Java.
  318. Automatic garbage collection is really convenient, so why wouldn't
  319. everyone want it?
  320. Well, the problem with automatic garbage collection is that:
  321. <P>
  322. <UL>
  323. <LI>
  324. automatic garbage collection often has a significant performance overhead, and
  325. <LI>
  326. automatic garbage collection causes performance to be unpredictable.
  327. <LI>
  328. automatic garbage collection is difficult to implement well (and thus
  329. expensive to implement well).
  330. </UL>
  331. <P>
  332. In Ada, the solution is to not require automatic garbage collection, but
  333. instead Ada is defined to explicitly permit automatic garbage collection
  334. and compiler vendors are free to implement it at their option.
  335. Ada does require that Unchecked_Deallocation be available, which will do
  336. nothing if there's an automatic garbage collector.
  337. If you're using an Ada compiler that doesn't do automatic garbage collection
  338. (true for most) and you're concerned about a bad deallocation, you
  339. can search for all uses of Unchecked_Deallocation.
  340. <P>
  341. Unchecked_Deallocation works just fine on any object, including arrays.
  342. <!-- This is different than C++, which silently causes bad things to happen -->
  343. <!-- if you use the wrong delete operation. -->
  344.  
  345.