[comp.lang.c++] multiple destructors

ttwang@polyslo.CalPoly.EDU (Thomas Wang) (10/01/89)

If I can have multiple constructors, why can't I have multiple destructors?

class foo
{
public:
  foo();
  foo(char);
  ~foo();
  ~foo(char);
  ~foo(int);
};

main()
{
  foo* foo1 = new foo('a');
  delete foo1('a');
}

jima@hplsla.HP.COM (Jim Adcock) (10/03/89)

//>If I can have multiple constructors, why can't I have multiple destructors?

/****

I believe that in the case of multiple constructors, the 
intent is to provide multiple ways of constructing one type of object.
When you are done constructing, you always end up with the same kind of
object, so there is of necessity only a need for one way to destroy it.

If you don't like this, one option is to put a construction-type field in
your object, and a destruction-type case statement in your destructor.
[But isn't this just an attempt to encode multiple types of objects into
one class?]

Alternately, maybe what you're really trying to address is multiple ways
of allocating space for a particular object, and thus need corresponding
ways to free the space associated with an object:

****/

//the following [weak] example either uses built-in new or malloc for space.
//it uses a dirty trick of initializing a field of foo in new.  is there a 
//better way?

#include <stddef.h>
#include <malloc.h>
#include <stdio.h>
#include <osfcn.h>

class foo
{
private: char newtype;
public:  foo(){}
public:
  void* operator new(size_t sz, char c='a');
  void operator delete(void* vp);
  ~foo() {/* do something to foo*/}
};

void* foo::operator new(size_t sz, char c)
{
  foo* fp;

  if (c == 'a') { fp = (foo*) ::operator new(sz); }
  else if (c == 'm') { fp = (foo*) malloc(sz); }
  else 
  {
    fprintf(stderr,"Error: presently only support foo::new('a' or 'm')\n");
    exit(1);
  }

  fp->newtype = c;
  printf("foo::new('%c') @ %X\n",c,fp);
  return (void*) fp;
}

void foo::operator delete(void* vp)
{
  foo* fp = (foo*) vp;
  printf("foo::delete flavor %c @ %X\n",fp->newtype, fp);
  if (fp->newtype == 'a') ::operator delete(vp); 
  else if (fp->newtype == 'm') free((char*)vp);
}


main()
{
  foo* foo0 = new foo;  	//use built-in memory allocator
  foo* foo1 = new('m') foo; 	//use malloc instead
  foo* foo2 = new('a') foo; 	//use built-in
  delete foo1;
  delete foo0;
  delete foo2;
}

wmm@sdti.com (William M. Miller) (10/19/89)

In article <6590273@hplsla.HP.COM> jima@hplsla.HP.COM (Jim Adcock) writes:
>//the following [weak] example either uses built-in new or malloc for space.
>//it uses a dirty trick of initializing a field of foo in new.  is there a 
>//better way?

It is a dirty trick; worse, it's not a general solution.  I wanted some
way of determining whether or not a given object was automatic, static,
or heap-based; my thought was to use operator new() in the way you did,
setting a flag to reveal the history of the object, but the problem is
that operator new() is not called for auto and static objects and the
garbage in auto objects could very well impersonate the flag left by
operator new() in a heap-based object.  I, too, join in a call for a
better way of doing this sort of thing.  At least in the old hack of
assigning to "this" you could tell whether the object was going into the
heap or whether storage had already been allocated for it.
-- 
Non-disclaimer:  My boss and I always see eye-to-eye (every time I look in
the mirror).

...!genrad!mrst!sdti!wmm

rfg@ics.uci.edu (Ron Guilmette) (10/22/89)

In article <1989Oct19.022635.123@sdti.com> wmm@sdti.SDTI.COM (William M. Miller) writes:
>In article <6590273@hplsla.HP.COM> jima@hplsla.HP.COM (Jim Adcock) writes:
>>//the following [weak] example either uses built-in new or malloc for space.
>>//it uses a dirty trick of initializing a field of foo in new.  is there a 
>>//better way?
>
>It is a dirty trick; worse, it's not a general solution.  I wanted some
>way of determining whether or not a given object was automatic, static,
>or heap-based; my thought was to use operator new() in the way you did,
>setting a flag to reveal the history of the object, but the problem is
>that operator new() is not called for auto and static objects and the
>garbage in auto objects could very well impersonate the flag left by
>operator new() in a heap-based object.

There is (I believe) a simple solution.  On most UNIX systems, the actual
memory areas occupied by automatic (stack) variables, static variables,
and "heap" variables are all different.  If you can get the starting and/or
ending addresses of these memory areas (and you usually can) then a quick
comparison of the address of a given object to these addresses will tell you
which area it is in.

For instance, on many systems, the virtual memory space of each process
looks like this:

		----------------  address 0xffffffff
		|              |
		|    stack     |
		|              |
                |..............|  <- SP
                |      |       |
                |      V       |
                |              |
                |              |
                |              |
                |              |
                |              |
                |      ^       |
                |      |       |
                |..............|  <- sbrk(0)
                |              |
                |    heap      |
                |              |
                |..............|  <- edata
		|              |
                |     .bss     |
                |              |
                |..............|
		|              |
		|    .data     |
                |              |
		|..............|  <- etext
		|              |
		|    .text     |
		|	       |
		----------------   address 0


So if the address of the object in question is less than the address of the
(pseudo) variable edata, then it is a "static" variable.  If it is greater,
but still less than the current value of sbrk(0) then it is a heap variable
(probably allocated via the new() operator).  If it is greater than the current
value of sbrk(0) (or greater than the value currently in your stack pointer
register) using an unsigned comparison then it is an "auto" variable.

Check your man pages for "edata", and "sbrk".

// rfg

bs@alice.UUCP (Bjarne Stroustrup) (10/22/89)

Ron Guilmette points out that by being clever you can deduce the storage
clas of an object from its address by knowing how your machine lays out
a program in memory. This is not only non-portable, but also unreliable
in the presense of a co-routine or lightweight process package because
these tend to have stacks in areas that to the simple single-thread layout
looks like free store.

ttwang@polyslo.CalPoly.EDU (Thomas Wang) (10/23/89)

bs@alice.UUCP (Bjarne Stroustrup) writes:

>Ron Guilmette points out that by being clever you can deduce the storage
>clas of an object from its address by knowing how your machine lays out
>a program in memory. This is not only non-portable, but also unreliable
>in the presense of a co-routine or lightweight process package because
>these tend to have stacks in areas that to the simple single-thread layout
>looks like free store.

This is a good point.  It seems there is no portable way in C++ to tell if
an object is automatic, heap, or static.

A related issue is the ability of C++ to support copying garbage collector.
To directly support garbage collected objects in C++ will introduce overhead
across the board.  But it is possible to make C++ friendly toward
copying garbage collector so that a 'class' implementation is possible.

Two problems are 'this' pointers, and destructors.

'this' pointers are a major problem.  There should be way to redefine 'this'
to another data type, prodived the data type has '->' operator to reach the
data members.

A 'handle' on the stack can be maintained to point to the right place.  A
physical pointer on the stack is impossible to maintain.

Destructors are a minor problem.  There should be an easy way to tell if
an object contains any destructors or not.  Currently this problem can be
solved by maintaining a virtual boolean function  has_destructor(), but it's
a tedious process.


 -Thomas Wang ("This is a fantastic comedy that Ataru and his wife Lum, an
                invader from space, cause excitement involving their neighbors."
                  - from a badly translated Urusei Yatsura poster)

                                                     ttwang@polyslo.calpoly.edu

wmm@sdti.com (William M. Miller) (10/24/89)

In article <1989Oct21.182759.13138@paris.ics.uci.edu> Ron Guilmette <rfg@ics.uci.edu> writes:
>There is (I believe) a simple solution.  On most UNIX systems, the actual
>memory areas occupied by automatic (stack) variables, static variables,
>and "heap" variables are all different.  If you can get the starting and/or
>ending addresses of these memory areas (and you usually can) then a quick
>comparison of the address of a given object to these addresses will tell you
>which area it is in.

This is not portable to non-Unix systems, so I'm afraid it's an even dirtier
trick than setting a flag in the allocated space.  (I'm using DOS and OS/2,
for example.)  Thanks for the thought, though.
-- 
Non-disclaimer:  My boss and I always see eye-to-eye (every time I look in
the mirror).

wmm@sdti.sdti.com

rfg@ics.uci.edu (Ron Guilmette) (10/28/89)

In article <1989Oct24.022726.2303@sdti.com> wmm@sdti.SDTI.COM (William M. Miller) writes:
>In article <1989Oct21.182759.13138@paris.ics.uci.edu> Ron Guilmette <rfg@ics.uci.edu> writes:
>>There is (I believe) a simple solution.  On most UNIX systems, the actual
>>memory areas occupied by automatic (stack) variables, static variables,
>>and "heap" variables are all different.  If you can get the starting and/or
>>ending addresses of these memory areas (and you usually can) then a quick
>>comparison of the address of a given object to these addresses will tell you
>>which area it is in.
>
>This is not portable to non-Unix systems, so I'm afraid it's an even dirtier
>trick than setting a flag in the allocated space.  (I'm using DOS and OS/2,
>for example.)  Thanks for the thought, though.

Bothe Bjarne and you are getting on my case because this ain't portable
(and because it will not work if you are in a multi-threaded system).

Cut me some slack guys!  I never said it was portable!  It is just *a*
solution.  It is better than the big nothing that the language gives you
if you *must* know what area an object is actually resident in.

P.S.  I'll bet that I can "port" this method you your XYZ system in less than
15 minutes (as long as the code which figures out what area an object is in
is nicely confined to one little area of your program).  As a matter of fact,
I'll bet that I could easily write an automatic configurator routine which
would (on startup) adapt to any arbitrary layout of stack/heap/static areas.
Does anybody believe that this would be hard?  If so, I'll be happy to
prove you wrong.

// rfg