From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,de1c23707584fc3c X-Google-Attributes: gid103376,public X-Google-ArrivalTime: 2003-04-22 10:28:41 PST Path: archiver1.google.com!postnews1.google.com!not-for-mail From: mheaney@on2.com (Matthew Heaney) Newsgroups: comp.lang.ada Subject: Re: virtual destructors Date: 22 Apr 2003 10:28:40 -0700 Organization: http://groups.google.com/ Message-ID: <1ec946d1.0304220928.72c2acc8@posting.google.com> References: NNTP-Posting-Host: 66.162.65.162 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit X-Trace: posting.google.com 1051032521 6857 127.0.0.1 (22 Apr 2003 17:28:41 GMT) X-Complaints-To: groups-abuse@google.com NNTP-Posting-Date: 22 Apr 2003 17:28:41 GMT Xref: archiver1.google.com comp.lang.ada:36356 Date: 2003-04-22T17:28:41+00:00 List-Id: "kat-Zygfryd" <6667@wp.pl> wrote in message news:... > 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."