comp.lang.ada
 help / color / mirror / Atom feed
From: mheaney@on2.com (Matthew Heaney)
Subject: Re: virtual destructors
Date: 22 Apr 2003 10:28:40 -0700
Date: 2003-04-22T17:28:41+00:00	[thread overview]
Message-ID: <1ec946d1.0304220928.72c2acc8@posting.google.com> (raw)
In-Reply-To: b83ivk$29l$1@news.onet.pl

"kat-Zygfryd" <6667@wp.pl> wrote in message news:<b83ivk$29l$1@news.onet.pl>...
> How can I define a virtual destructor in Ada95?

There are two ways.

I. Use controlled types

Have your type (privately) derived from Controlled, and then use
Unchecked_Deallocation to delete it through a class-wide type:

package P is

   type T is tagged private;

   type T_Class_Access is access all T'Class;

   procedure Op (O : access T);

private

   type T is new Controlled with record
      ...;
   end record;

   procedure Finalize (O : in out T);

end P;


package P.C is

   type NT is new T with private;

private

   type NT is new T with record
      ...;
   end record;

   procedure Finalize (O : in out NT);

end P.C;

procedure Free is 
  new Ada.Unchecked_Deallocation (T'Class, T_Class_Access);

declare
   O1 : T_Class_Access := new T;
   O2 : T_Class_Access := new NT;
begin
   Free (O1);  --call P.Finalize
   Free (O2);  --call P.C.Finalize
end;


What's happening here is that the Ada run-time system knows that types
in T'Class are controlled, so that when you use
Unchecked_Deallocation, it calls the Finalize operation for the object
being deleted.

So in this sense destructors are already dispatching in Ada95, and
there's nothing special you need to do.

Note that Finalize operations for a class are typically implemented by
performing actions specific to the derived type, and then forwarding
the call to the parent type, e.g.

procedure Finalize (O : in out NT) is
begin
  --do NT stuff here
  Finalize (T (O)); --"view conversion" to call T's Finalize
end;

Also note that if any components are controlled, they too will get
finalized in the normal way.  For example, if we had implemented T
this way:

private
  type T is tagged record -- not itself controlled
     Vector : Vector_Subtype;
  end record;

Here, the Vector_Subtype is an instance of
Charles.Vectors.Unbounded.Container_Type, which is implemented as a
controlled type.  So when an object of T is deallocated, controlled
finalization of its component(s) occurs, which means here that the
Finalize for Vector is called automatically.

What this really means is that you may not need a virtual destructor
at all.


II. Use an explicit dispatching destructor

In this case you're going to assume some of the duties yourself, by
explicitly calling a dispatching operation.

What many Ada programmers don't seem to realize is that operations
that have access parameters are primitive for a type, meaning that
they are inherited during derivations, and (more importantly here)
dispatch when called with a parameter whose type is class-wide.

Let's modify our class above to add a private primitive operation:

package P is
   type T (<>) is tagged limited private;
   type T_Class_Access is access all T'Class;
   function New_T return T_Class_Access;
   procedure Free (O : in out T_Class_Access);
private
   type T is tagged limited record ...;
   procedure Do_Free (O : access T); --declare primitive
end P;

package P.C is
  type NT (<>) is new T with private;
  function New_NT return T_Class_Access;
private
  type NT is new T with record ...;
  procedure Do_Free (O : access NT); --override primitive
end P.C;

What we want to do is hand off the pointer to the object to the
destructor, but we need to the destructor to dispatch.  The public
Free operation is implemented this way:

procedure Free (O : in out T_Class_Access) is
begin
  if O /= null then
     Do_Free (O);  -- dispatches 
     O := null;
  end if;
end Free;

The object designated by pointer O has type T'Class, which means that
if called with a primitive operation, then dispatching will occur. 
Do_Free is primitive, so it dispatches.  For example:

declare
  O1 : T_Class_Access := New_T;
  O2 : T_Class_Access := New_NT;
begin
  Free (O1); --internal dispatch to T's Do_Free
  Free (O2); --internal dispatch to NT's Do_Free
end;

The Free operation is an example of the "template method" pattern. 
The Do_Free operation is the "virtual destructor."



  parent reply	other threads:[~2003-04-22 17:28 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-04-22 14:18 virtual destructors kat-Zygfryd
2003-04-22 14:27 ` Stephen Leake
2003-04-22 14:56   ` kat-Zygfryd
2003-04-22 15:45     ` kat-Zygfryd
2003-04-22 16:34       ` tmoran
2003-04-22 21:32         ` Robert A Duff
2003-04-22 17:18       ` Stephen Leake
2003-04-22 16:34         ` Simon Wright
2003-04-22 19:57           ` Stephen Leake
2003-04-22 20:19             ` Simon Wright
2003-04-22 21:23             ` Robert A Duff
2003-04-23 15:16         ` Matthew Heaney
2003-04-22 17:31       ` virtual destructors - doesn't seem to work kat-Zygfryd
2003-04-22 17:32         ` Simon Wright
2003-04-22 17:33     ` virtual destructors Matthew Heaney
2003-04-22 17:28 ` Matthew Heaney [this message]
2003-04-22 17:40   ` kat-Zygfryd
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox