home *** CD-ROM | disk | FTP | other *** search
- <COMMENT This is a lesson file for the Lovelace Ada tutorial>
- <COMMENT A program called genlesson is used to transform this file into a set>
- <COMMENT of useful HTML files for use by Mosaic & other WWW browsers.>
-
- <COMMENT Edit the following lines. >
- <TUTOR NAME="Lovelace">
- <LESSON NUMBER=12>
- <AUTHOR NAME="David A. Wheeler" EMAIL="wheeler@ida.org">
- <AUTHOR ADDRESS="<A HREF="dwheeler.htm">David A. Wheeler (wheeler@ida.org)</A>">
- <COMMENT $Id: lesson12.les,v 1.2 1995/04/28 20:32:53 wheeler Exp $ >
-
- <COMMENT You'll probably want to uncomment and edit these lines: >
- <COMMENT <PREVIOUS_LESSON LOCATION="URL_of_directory/" >
- <COMMENT <NEXT_LESSON LOCATION="URL_of_directory/" >
-
- <COMMENT A lesson is divided into 1 or more "sections".>
- <COMMENT Each section has a title; SECTION starts a new section.>
-
- <SECTION NAME="Basics of Access Variables">
- It is often very useful to have a variable that, instead of storing
- a value, stores a reference to some other object or a subprogram.
- Such variables are called <EM>access</EM> variables in Ada, and
- are essentially equivalent to pointers or references in other languages.
- One common use of access variables is to implement items of varying size.
- <P>
- To create such variables, first create a type for it, called an
- access type.
- <P>
- Here's an example of an access type declaration,
- declaring that variables of type Node_Access
- can access (reference) objects of type Node:
- <P>
- <PRE>
- type Node_Access is access Node;
- </PRE>
- <P>
- Here is the
- <A HREF="bnf.htm">BNF</A> for creating an access type to an object:
- <P>
- <PRE>
- access_object_type_declaration ::= "type" new_type_name "is"
- "access" ["all"] type_name ";"
- </PRE>
- <P>
- Variables of access type can either refer to an object or be
- <EM>null</EM>.
- You can make an access variable null by assigning it the value of the
- keyword "null", and you can check if it's null by comparing the variable
- (using "=" or "/=") to "null".
- <P>
- The ability to "point" at other values is useful and efficient, but it
- can also be dangerous.
- It's easy to do the wrong thing with pointers and cause surprising results.
- Ada tries to limit the damage that you can do while maintaining efficiency.
- Ada does this through the following rules:
- <UL>
- <LI>
- All access type variables are initialized as null (unless you specifically
- initialize them to something else).
- <LI>
- All operations that use what an access value references first check
- to see if the access value is null.
- If the access value is null, the exception Constraint_Error is raised.
- <LI>
- Normally access-to-object types are limited to referring to
- objects ``created dynamically'', as we will discuss next.
- You must add the keyword ``all'' to permit an object type to refer
- to all objects of a given type; such access values are called
- <EM>general</EM> access objects and are new to Ada 95.
- General access objects are a little dangerous than regular access types,
- so it's best to avoid using them where practical.
- One use for general access objects is to interface with C or C++ programs,
- since C and C++ pointers are equivalent to Ada general access objects.
- Another important use for general access objects is for object-oriented
- programming, as we'll discuss later.
- </UL>
- <P>
- The Ada compiler will optimize these checks and initializations away
- if it can determine that they're unnecessary.
- You can also turn off these checks for a given subprogram if you know
- that that particular subprogram is totally correct.
- <P>
- Now that we know how to declare access types, let's see how we can use them.
-
- <SECTION NAME="Simple Uses of Access Variables">
- Let's imagine that we want to create a binary tree of Unbounded_Strings.
- A binary tree is a set of nodes; each node has a way to locate its
- parent, its left child node, and a right child node.
- Each node also has data it contains (an Unbounded_String).
- Defining a record for a binary tree node using access values is easy:
- <P>
- <PRE>
- type Tree_Node is
- record
- Parent : Tree_Access;
- Left, Right : Tree_Access;
- Data : Unbounded_String;
- end record;
- </PRE>
- <P>
- Of course, defining Tree_Node depends on
- a type called Tree_Access, which depends on a type called Tree_Node.
- Note the circularity - each type's definition depends on the other.
- The way to solve this is to use an ``incomplete type declaration''
- of the record (this is the same thing you'd do in C or Pascal).
- An incomplete type declaration has the keyword "type", the name of the
- type that you plan to declare later, and an immediately following semicolon.
- For example, before defining Tree_Node you'd do the following:
- <P>
- <PRE>
- type Tree_Node; -- Incomplete type declaration.
- type Tree_Access is access Tree_Node;
- </PRE>
- <P>
- You can then declare variables of access types using the normal
- variable declaration syntax, for example, you can create two
- access variables this way:
- <P>
- <PRE>
- Current, Root : Tree_Access;
- </PRE>
- <P>
- What we need is an operation that will create a new Tree_Node object
- (in a general storage area) and then return an
- access value referencing this newly-created object.
- The process of creating a new object this way is called <EM>allocation</EM>
- In C the allocation operation is called "malloc", while in Pascal and
- C++ it's called "new".
- Ada also calls this operation <EM>new</EM>, and Ada has the same
- syntax as C++ and Pascal;
- the keyword new is followed by the name of the type to be created.
- For example, here's how to create a new object of type
- Tree_Node, setting the value of "Current" to access it:
- <P>
- <PRE>
- Current := new Tree_Node;
- </PRE>
- <P>
- Once an access value references a real object (instead of being null), you
- can use the ``dot'' operation to refer to the object pointed to by the access
- value.
- This is the same syntax as when accessing values in a record.
- For example, here's how to set the data value of the Node being pointed to
- by Current:
- <P>
- <PRE>
- Current.Data := To_Unbounded_String("Hello!");
- </PRE>
- <P>
- In some cases you want to work with the "entire" object being
- accessed instead of a piece of it. For example, let's say that you
- have some procedure (My_Procedure) that requires as input a Tree_Node.
- You can't just pass in an access value to a Tree_Node, because the
- types are different (an access value is different than what it accesses).
- To handle this, simply use the word "all" after the dot operation
- to refer to the entire object:
- <P>
- <PRE>
- My_Procedure(Current.all);
- </PRE>
- <P>
- <P>
- Finally, you can have multiple different pointers referring to the same object.
- This is called access assignment; all you need to do is use ordinary
- assignment statements.
- For example, to set the access value "Root" so that it references the
- same node as "Current", simply do:
- <P>
- <PRE>
- Root := Current;
- </PRE>
- <P>
-
- <QUESTION Type=Multiple-Choice>
- If you execute the statements listed above, what is the value of Root.Data?
- <CHOICES>
- <CHOICE ANS=1>An empty string.
- <CHOICE ANS=2>"Hello!"
- <CHOICE ANS=3>None; an exception would be raised.
- </CHOICES>
- <ANSWER ANS=2>
- <RESPONSES>
- <WHEN ANS=1>
- No, sorry.
- <WHEN ANS=2>
- Quite right.
- Root now references the same object as "Current" does, and the
- Tree_Node that both of them reference has "Hello!" as its value in Data.
- <WHEN ANS=3>
- Sorry.
- If we hadn't set "Root := Current" an exception would have been
- raised, but now "Root" references the same object as "Current" does.
- Please try again.
- </RESPONSES>
-
- <SECTION NAME="Access Parameters (For Object Orientation)">
-
- Often when developing object-oriented systems you will want to
- pass around access values to tagged types.
- Ada 95 adds a new pseudo-mode called "access" to help you build OO
- systems using access types.
- <P>
- If you recall, every parameter for a subprogram has to be of mode
- <I>in</I>, <I>in out</I>, or <I>out</I>.
- You can use the keyword "access" as a mode (followed by a type name) instead.
- Here's an example:
- <P>
- <PRE>
- procedure Get(Agent : access Occupant; Direct_Object : access Occupant'Class);
- </PRE>
- <P>
- So what in the world does this example mean??
- Here's the answer:
- <UL>
- <LI>
- When an "access mode" is followed by an ordinary tagged type,
- it means that the input parameter (in this case Agent) must be
- an access value of the given type (in this case Occupant).
- More importantly, this procedure can be overridden, and the
- type of the <I>object being accessed</I> will determine which subprogram
- to dispatch to.
- Thus, we could create another subprogram called "Get" for a descendent
- of Occupant, and that subprogram Get would override the Get defined here.
- This is an essential part of being an OO language - we can dispatch to
- a given program using the current data value.
- Access parameters let us dispatch using the access value.
- <LI>
- When an "access mode" is followed by an class type,
- it means that the input parameter (in this case Direct_Object) must be
- an access value of the given type (in this case Occupant) or <I>any</I>
- of its descendents.
- In this case we do <I>not</I> dispatch on this parameter,
- since any of the descendents will do for this subprogram.
- In this case, access parameters let us accept a wide range of types,
- instead of just a specific access type.
- </UL>
- <P>
- There's an important requirement for access parameters - null values are
- not permitted.
- If you want to permit null values, use the modes in, out, or in out with
- an ordinary access type.
- <P>
- It's difficult to understand access parameters
- without more context, so we'll defer discussing this further until
- <A HREF="lesson18.htm">lesson 18</A> where we will
- look at examples of this.
- What you need to understand right now is that if you're using access types
- and object-oriented programming, you will probably want to use
- the pseudomode "access".
-
- <SECTION NAME="Unchecked_Deallocation">
- Over time it's possible that some objects will no longer
- be referenced.
- Handling this case is called ``garbage collection.''
- Ada was designed to permit, but not require, automatic garbage collection.
- Since most Ada compilers do not perform automatic garbage collection,
- it's more portable to assume that garbage collection won't be performed
- automatically.
- Note that all Ada compilers that generate
- <A HREF="http://www.yahoo.com/Computers/Languages/Java">Java</A> J-code
- (bytecodes)
- <EM>do</EM> perform automatic garbage collection.
- <P>
- Ada provides a generic procedure to release an object if it's no
- longer being referenced.
- This procedure is equivalent to ``free'' in C and ``delete'' in C++.
- This generic procedure's name is ``Unchecked_Deallocation''.
- Since it's a generic, you need to instantiate the generic using the
- access type you're using.
- By convention, the name of the instantiation is usually called "Free".
- <P>
- Here's the official definition of generic procedure
- Unchecked_Deallocation:
- <P>
- <PRE>
- generic
- type Object(<>) is limited private;
- type Name is access Object;
- procedure Unchecked_Deallocation(X : in out Name);
- </PRE>
- <P>
- Note that we need to pass it two things; a type, and the access to that type.
- Here's a simple example - let's instantiate a procedure called ``Free''
- that will let us release objects when they're no longer used:
- <P>
- <PRE>
- procedure Free is new Unchecked_Deallocation(Tree_Node, Tree_Access);
- </PRE>
- <P>
- Now that we've instantiated a procedure called ``Free'', we can call it.
- Let's continue our example; imagine that we don't want to use
- the node we created in the last section any more.
- That's fine, we'll just call the new ``Free'' routine we've created:
- <P>
- <PRE>
- Free(Current);
- </PRE>
- <P>
- When Free returns, the variable Current will have the value "null", and
- the memory that used to be used by the Tree_Node will have been released.
- Any instantiation of Unchecked_Deallocation will automatically call
- any finalization operations defined for the enclosed types, as you would expect.
- <P>
- An important problem arises here that also arises in C, C++, and Pascal:
- what if there's another access type that refers to that object?
- In our example, the access variable ``Root'' still refers to an object,
- but that object no longer exists.
- Any attempt to use Root to access its object may result in unpredictable
- behavior.
- While Ada provides a number of protections in the use of access variables,
- this is one problem Ada doesn't completely protect against.
- <P>
- This is one area where there is a strong tension between the desire
- to be safe and easy to use versus the desire to be predictably efficient.
- Some languages require deallocation to be handled automatically;
- this is called <EM>automatic garbage collection</EM>.
- Examples of such languages include Smalltalk, Eiffel, and Java.
- Automatic garbage collection is really convenient, so why wouldn't
- everyone want it?
- Well, the problem with automatic garbage collection is that:
- <P>
- <UL>
- <LI>
- automatic garbage collection often has a significant performance overhead, and
- <LI>
- automatic garbage collection causes performance to be unpredictable.
- <LI>
- automatic garbage collection is difficult to implement well (and thus
- expensive to implement well).
- </UL>
- <P>
- In Ada, the solution is to not require automatic garbage collection, but
- instead Ada is defined to explicitly permit automatic garbage collection
- and compiler vendors are free to implement it at their option.
- Ada does require that Unchecked_Deallocation be available, which will do
- nothing if there's an automatic garbage collector.
- If you're using an Ada compiler that doesn't do automatic garbage collection
- (true for most) and you're concerned about a bad deallocation, you
- can search for all uses of Unchecked_Deallocation.
- <P>
- Unchecked_Deallocation works just fine on any object, including arrays.
- <!-- This is different than C++, which silently causes bad things to happen -->
- <!-- if you use the wrong delete operation. -->
-
-