Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!usc!samsung!munnari.oz.au!metro!otc!grahamd From: grahamd@otc.otca.oz.au (Graham Dumpleton) Newsgroups: comp.lang.c++ Subject: Re: Distinguishing stack and heap objects Message-ID: <2147@otc.otca.oz> Date: 9 Jan 91 23:02:19 GMT References: <1990Dec29.013620.23761@sjsumcs.sjsu.edu> <1991Jan7.231403.318@lia> Sender: news@otc.otca.oz Reply-To: grahamd@otc.otca.oz.au (Graham Dumpleton) Organization: OTC Development Unit, Australia Lines: 123 In article <1991Jan7.231403.318@lia>, jgro@lia (Jeremy Grodberg) writes: |> In article <1990Dec29.013620.23761@sjsumcs.sjsu.edu> horstman@sjsumcs.SJSU.EDU (Cay Horstmann) writes: |> >Give class X a member int fromHeap and a static member int heapFlag. Define |> >all constructors X::X( ... ) to contain the lines |> > fromHeap = heapFlag; |> > heapFlag = 0; |> >and define X::operator new( size_t s ) as |> > { heapFlag = 1; |> > return new char[s]; |> > } |> > |> >If x is any pointer of type X*, then x->fromHeap is true iff x is allocated |> >on the heap. I am using the fact that X::operator new is (acts like?) a |> >static member function and hence can access static members of X. |> > |> >Is this legal? Is it in the spirit of the language? Is it useful? CAN I PATENT |> >IT? |> |> Yes. |> Maybe. |> No. |> No. |> |> |> The real problem here is that whle this will work for |> |> X* x = new X(...); |> |> It will not catch |> |> X* x = new X[10]; |> |> since that will use ::new, not X::new. |> |> -- |> Jeremy Grodberg "I don't feel witty today. Don't bug me." |> jgro@lia.com Depending on the use to which you are putting this information, knowing that something is created within an array of objects on the heap is not required and if it was possible to find this out, would have to be distinguished from not being in an array. Consider a class for performing reference counting; something like that defined in the InterViews class library. class Resource { private: u_int myNumRefs; protected: Resource() : myNumRefs(0) { } virtual ~Resource() { } public: void reference() { myNumRefs++; } void unReference() { myNumRefs--; if (myNumRefs == 0) delete this; } u_int numRefs() { return myNumRefs; } }; One problem with this class; as it is, is that if you create an instance of a class derived from it on the stack, then you can run into problems. class Foo : public Resource { ... }; void func() { Foo foo; foo.reference(); foo.unReference(); // foo destroyed. } // foo destroyed again. A destructor which gets called twice could cause major problems in a program depending on what it actually did. Yes, you could say that you must always create instances of a class derived from a Resource on the heap, i.e: void func() { Foo* foo = new Foo; foo->reference(); foo->unReference(); // foo destroyed } or void func() { Foo& foo = *new Foo; foo.reference(); foo.unReference(); // foo destroyed however this doesn't stop people from trying to reference an item which appears on the stack if created that way. We came up with the solution of embodying the concept of determining if an object is created on the free store or not in a seperate class and then deriving Resource from it. The reference() member would then check to make sure the object has been created on the free store before doing anything, if it wasn't it would cause a fatal error. class Alloc { public: // ... bool allocatedUsingNew(); }; class Reference : public Alloc { public: // ... void reference() { if (allocatedUsingNew()) myNumRefs++; else { abort(); cerr << "Object not a free standing object on the heap." << endl; } } }; It should be noted that this doesn't stop you from creating instances of Foo on the stack, it just stops you from trying to reference it. Now to consider the point which you originally brought up about arrays of objects on the heap. As you state, overloading operator new() to find out if objects are created on the free store doesn't pick up the case of objects as part of arrays. If it did however, the scheme which I outline above would fail with the same sort of problem as objects being created on the stack. Namely the destructor would be called twice, first when you perform an unReference() and secondly when delete the array of objects. A w orse case actually exists if the array is deleted before an unReference() occurs, as the actual space which the this pointer would be set doesn't exist anymore. Similar problems also arise when considering objects which are members of another class, however I won't go into that here. Finally just to put into perspective why we use this scheme anyway, it is because we have created a class library which is as robust as possible We sacrifice some efficiency in the process, however we would prefer to have the program abort and give some message describing what programming principle you violated as opposed to a straight core dump occuring, with absoulutely no idea what went wrong. Graham Dumpleton (grahamd@otc.otca.oz.au)