[comp.lang.c++] Constructors and new

dmg@ssc-vax.UUCP (David Geary) (05/31/89)

  According to BS, section 5.5.7, a constructor for a class can determine whether
it was called by new or not.

"If it is called by new, the pointer this has the value zero at entry,
otherwise this points to space already allocated for the object..."

  Ok, so what does the following print?

#include <stdio.h>

class testclass
{
  public:

  testclass()   { if(this == 0) puts("Used new..."); else puts("Did not use new..."); }
 ~testclass()   { puts("I'm a destructor..."); }
};

main()
{
  testclass   var;
  new testclass;
}

Output:  

Did not use new...
Did not use new...
I'm a destructor...

 This tells me one of two things:

 1)  I've found a bug in the compiler.
 2)  I don't know what the h*ll I'm doing.

  My ego tells me #1, but my experience tells me it has to be #2 ;-)

-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ David Geary, Boeing Aerospace, Seattle                 ~ 
~ "I wish I lived where it *only* rains 364 days a year" ~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

dld@F.GP.CS.CMU.EDU (David Detlefs) (05/31/89)

Dave Geary asks why this program:

#include <stdio.h>

>class testclass
>{
>  public:
>
>  testclass()   { if(this == 0) puts("Used new..."); else puts("Did not use new..."); }
> ~testclass()   { puts("I'm a destructor..."); }
>};
>
>main()
>{
>  testclass   var;
>  new testclass;
>}

produces this output:

>Did not use new...
>Did not use new...
>I'm a destructor...

I believe I can explain this.  Look at 'main.'  The first line of the
output is caused by the constructor call to initialize 'var', a
stack-allocated automatic variable.  The second line is caused by the
evaluation of the expression 'new testclass;'  This *does* use new, so
why does it say it doesn't?  Well if you look at testclass, you'll
find that sizeof(testclass) is 0.  I would guess that the 'malloc' that
operator new is calling returns 0 when it is asked to allocate a
zero-sized object.  Thus, 'this' is 0 on entry to the constructor.
Finally, the testclass destructor is called when 'var' goes out of
scope.

Hope this helps.

Dave

--
Dave Detlefs			Any correlation between my employer's opinion
Carnegie-Mellon CS		and my own is statistical rather than causal,
dld@cs.cmu.edu			except in those cases where I have helped to
				form my employer's opinion.  (Null disclaimer.)
-- 

leo@atcmp.nl (Leo Willems) (06/02/89)

>
>Dave Geary asks why this program:
>
>
>>class testclass
>>{
>>  public:
>>
>>  testclass()   { if(this == 0) puts("Used new..."); else puts("Did not use new..."); }
>> ~testclass()   { puts("I'm a destructor..."); }
>>};
>>
>>main()
>>{
>>  testclass   var;
>>  new testclass;
>>}
>
>produces this output:
>
>>Did not use new...
>>Did not use new...
>>I'm a destructor...

In the paper `the evolution of C++ 1985 to 1987' Bjarne Stroustrup gives
a more detailed example of the way how to use the -this- pointer
in a constructor when allocating memory yourself. (versus par. 5.5.7 in 
the C++ P.L.)
Somewhere (I can't find it  right now) it is mentioned that the
assignment to the -this- pointer in the constructor should be made on
every possible execution path.
The following code does not suffer from the problem above:

extern char*malloc(int);
extern void free(char*);

class testclass
{
private:
	int used_new;
public:
	testclass();
	~testclass(){
		if (used_new){
			free((char*)this);
			this = 0;
				// this assignment must be done
		}
		puts("I'm a destructor...");
	}
};

testclass::testclass()
{
		if (this == 0){
			 puts("Used new...");
			 this = (testclass*) malloc(sizeof(testclass));
				// this assignment must be done
			 used_new = 1;	//assigment after allocation!
		 } else {
			used_new = 0;
		 	puts("Did not use new...");
		 	this = this;
				// this assignment must be done
		 }
}

main()
{
	testclass   var;
	new testclass;
}


This solution leaves me with a question too.

Shouldn't the destructor also assign on every possible path to the
-this- pointer?

Leo Willems				Internet: leo@atcmp.nl
AT Computing				UUCP:     mcvax!hp4nl!kunivv1!atcmpe!leo
P. O. Box 1428				
6501 BK  Nijmegen
The Netherlands

wmm@sdti.SDTI.COM (William M. Miller) (06/02/89)

In article <2684@ssc-vax.UUCP> dmg@ssc-vax.UUCP (David Geary) writes:
>  According to BS, section 5.5.7, a constructor for a class can determine whether
>it was called by new or not.
That only works if the constructor assigns to `this.'  (That's the context
of the statement in 5.5.7 you mention, although the connection is not as
clear as it ought to be.)  What happens is that, if there is an assignment
to `this' in the body of the constructor, the value of `this' is left as
it is upon entry until that assignment occurs: zero if allocated via `new,'
nonzero for static and auto.  If there is no assignment to `this,' the
compiler inserts code at the beginning of the constructor, before any of
the statements in the body, to check if `this' is zero and do the allocation
via operator new() if needed.  The allocation and assignment to `this' have
already occurred before the test in your example constructor.

This question illustrates the reason assignment to `this' is replaced in
version 2.0: it's confusing, unclear, error-prone, etc.

kt@msor.UUCP (Keith Tizzard) (06/02/89)

In article <2684@ssc-vax.UUCP> dmg@ssc-vax.UUCP (David Geary) writes:
:
:  According to BS, section 5.5.7, a constructor for a class can determine whether
:it was called by new or not.
:
:"If it is called by new, the pointer this has the value zero at entry,
:otherwise this points to space already allocated for the object..."
:
:  Ok, so what does the following print?
:
:#include <stdio.h>
:
:class testclass
:{
:  public:
:
:  testclass()   { if(this == 0) puts("Used new..."); else puts("Did not use new..."); }
: ~testclass()   { puts("I'm a destructor..."); }
:};
:
:main()
:{
:  testclass   var;
:  new testclass;
:}
:
:Output:  
:
:Did not use new...
:Did not use new...
:I'm a destructor...
:
: This tells me one of two things:
:
: 1)  I've found a bug in the compiler.
: 2)  I don't know what the h*ll I'm doing.
:
:  My ego tells me #1, but my experience tells me it has to be #2 ;-)
:
:-- 
:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:~ David Geary, Boeing Aerospace, Seattle                 ~ 
:~ "I wish I lived where it *only* rains 364 days a year" ~
:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Zortech C++ produces the following output:-


Used new...
Used new...
I'm a destructor...


: From: dld@F.GP.CS.CMU.EDU (David Detlefs)
: References: <2684@ssc-vax.UUCP>
: 
: 
: And in <2684@ssc-vax.UUCP> David Detlefs adds :
: 
: 
: I believe I can explain this.  Look at 'main.'  The first line of the
: output is caused by the constructor call to initialize 'var', a
: stack-allocated automatic variable.  The second line is caused by the
: evaluation of the expression 'new testclass;'  This *does* use new, so
: why does it say it doesn't?  Well if you look at testclass, you'll
: find that sizeof(testclass) is 0.  I would guess that the 'malloc' that
: operator new is calling returns 0 when it is asked to allocate a
: zero-sized object.  Thus, 'this' is 0 on entry to the constructor.
: Finally, the testclass destructor is called when 'var' goes out of
: scope.


So I added an integer in the private part of the class and Zortech
then produced:


Did not use new...
Did not use new...
I'm a destructor...


So where does this get us?  :-)

vasta@apollo.COM (John Vasta) (06/03/89)

In article <2684@ssc-vax.UUCP> dmg@ssc-vax.UUCP (David Geary) writes:
>
>  According to BS, section 5.5.7, a constructor for a class can determine whether
>it was called by new or not.
>
>"If it is called by new, the pointer this has the value zero at entry,
>otherwise this points to space already allocated for the object..."
>
>  Ok, so what does the following print?
>
>#include <stdio.h>
>
>class testclass
>{
>  public:
>
>  testclass()   { if(this == 0) puts("Used new..."); else puts("Did not use new..."); }
> ~testclass()   { puts("I'm a destructor..."); }
>};
>
>main()
>{
>  testclass   var;
>  new testclass;
>}
>
>Output:  
>
>Did not use new...
>Did not use new...
>I'm a destructor...
>
> This tells me one of two things:
>
> 1)  I've found a bug in the compiler.
> 2)  I don't know what the h*ll I'm doing.
>
>  My ego tells me #1, but my experience tells me it has to be #2 ;-)
>
>-- 
>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>~ David Geary, Boeing Aerospace, Seattle                 ~ 
>~ "I wish I lived where it *only* rains 364 days a year" ~
>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Checking the value of 'this' only makes sense if you want to assign to it.
The statement from The Book is in the section about taking over the storage
allocation mechanism; the first sentence in 5.5.7 is

"When assigning to this in a constructor, the value of this is undefined
until that assignment."

If you don't assign to this, the compiler synthesizes code to do so (if this == 0).
The code is inserted at the beginning of the constructor, so the first statements
you write are guaranteed to never see this == 0, unless you assign to this
somewhere, or there's some sort of storage allocation failure.

If you change the constructor to read:

testclass()   { if(this == 0) puts("Used new..."); else puts("Did not use new...");
                this = this; }

then the output from the program is:

Did not use new...
Used new...
I'm a destructor...

-- 
John Vasta                Apollo Computer, Inc.
vasta@apollo.com          M.S. CHA-01-LT
(508) 256-6600 x6362      330 Billerica Road, Chelmsford, MA 01824
UUCP: {decwrl!decvax, mit-eddie, attunix}!apollo!vasta

ttwang@polyslo.CalPoly.EDU (Thomas Wang) (06/03/89)

In article <363@msor0.UUCP> kt@msor.UUCP (Keith Tizzard) writes:
>According to BS, section 5.5.7, a constructor for a class can determine whether
>it was called by new or not.
>"If it is called by new, the pointer this has the value zero at entry,
>otherwise this points to space already allocated for the object..."

>#include <stdio.h>
>class testclass
>{ public:
>testclass()   { if(this == 0) puts("Used new..."); else puts("Did not use new..."); }
> ~testclass()   { puts("I'm a destructor..."); }
>};
>main() {
>  testclass   var;
>  new testclass; }

Testing (this == 0) only works if you set 'this' to some value inside the
constructor.  I have a non-portable hack though.

testclass::testclass()
{
  int dummy;
  if (this < &dummy) puts("Used new\n");
  else puts("did not use new\n");
}

This hack depends on the fact that stack and heap usually grows into each
other from opposite side of memory.


 -Thomas Wang (Mak-Kuro Kurosuke, come on out!  If you don't come out,
               we'll pull your eyeballs out!
                                      - as heard in Tonari No Totoro )

                                                     ttwang@polyslo.calpoly.edu

fredriks@kuling.UUCP (Fredrik Stax{ng) (06/04/89)

I also had this problem when I tried to implement reference counting.

Because I wanted the class to be derivable I couldn't use the malloc
hack (and did not want to), but changed the new() function to set the
pointer new_last to the last new'ed object.
I think there should be a defined way of doing this.

I also encountered another problem. I wanted to get the pointer class
to behave as a native pointer, but I didn't manage to get -> operator
working. If the compiler had recognized the equivalence a->b <=> (*a).b
this had been easy.



-- 
Fredrik Stax{ng                | C looks like a mix of Algol and TECO.
CS Student@Uppsala University  |              -- KPJ Jaakkola
fredriks@kuling.docs.uu.se     |

pj@hrc63.co.uk (Mr P Johnson "Baddow") (06/26/89)

In article <43984c6e.1ad5a@apollo.COM>, vasta@apollo.COM (John Vasta) writes:
> 
> Checking the value of 'this' only makes sense if you want to assign to it.
> The statement from The Book is in the section about taking over the storage
> allocation mechanism; the first sentence in 5.5.7 is
> 
> "When assigning to this in a constructor, the value of this is undefined
> until that assignment."

Thats in The Book, but Stroustrup has written "The Evolution of C++" which
describes member operator functions for new and delete which do this sort of
think much more neatly.

At least one compiler (Oregon) now does it this way.
-- 
Paul Johnson,         | `The moving finger writes, And having writ, moves on,'
GEC-Marconi Research  |                    Omar Kyham when contemplating `vi'.
------------------------------------------------------------------------------
The company has put a radio inside my head: it controls everything I say!