Am I on the heap? & Virtual Functions

Started by
36 comments, last by aboeing 20 years, 6 months ago
Hi, I am trying to write a class to let me know if the object has been constructed on the heap. The following code works:

class HeapTest {
public:
	HeapTest() {
		printf("0x%x HeapTest alive!\n",this);
	}
	~HeapTest() {
		printf("0x%x goodbye from HeapTest!\n",this);
	}
	static void *operator new (size_t size) {
		void *memPtr=::operator new(size);
		printf("0x%x heap!\n",memPtr);
		return memPtr;
	}
	static void *operator new[] (size_t size) {
		void *memPtr=::operator new(size);
		printf("0x%x heap array!\n",memPtr);
		printf("sizeof(me):%d\n",sizeof(HeapTest));
		printf("therefore, i am creating %d of me\n",(size/sizeof(HeapTest))-1);
		printf("therefore the following are on the heap:\n");
		char *htp=(char *) memPtr;
		for (unsigned int i=1;i<size/sizeof(HeapTest);i++)
			printf("0x%x (%d)\n",htp+i*sizeof(HeapTest),i);
		return memPtr;
	}
	void SayHi(char *msg) {
		printf("0x%x hi:%s\n",this,msg);
	}
	int x;	

};

int main() {

	HeapTest ht;
	ht.SayHi("ht");

	HeapTest *htp = new HeapTest;
	htp->SayHi("htp");
	delete htp;

	HeapTest *htpa = new HeapTest[3];
	htpa[2].SayHi("htpa[2]");

	delete [] htpa;
	printf("eop\n");
}
Now the thing is, I wanted to use this as a base class so that I could let my memory manager correctly delete things. But when I add virtual functions everything goes wrong. Any solutions? Thanks.
Advertisement
Heres the program output btw:

0x12ff70 HeapTest alive!
0x12ff70 hi:ht
0x431b70 heap!
0x431b70 HeapTest alive!
0x431b70 hi:htp
0x431b70 goodbye from HeapTest!
0x431b60 heap array!
sizeof(me):4
therefore, i am creating 3 of me
therefore the following are on the heap:
0x431b64 (1)
0x431b68 (2)
0x431b6c (3)
0x431b64 HeapTest alive!
0x431b68 HeapTest alive!
0x431b6c HeapTest alive!
0x431b6c hi:htpa[2]
0x431b6c goodbye from HeapTest!
0x431b68 goodbye from HeapTest!
0x431b64 goodbye from HeapTest!
eop
0x12ff70 goodbye from HeapTest!
quote:Hi, I am trying to write a class to let me know if the object has been constructed on the heap.

Then make the constructor private and construct objects through a factory that returns objects that have been newed into existance.
char a[99999],*p=a;int main(int c,char**V){char*v=c>0?1[V]:(char*)V;if(c>=0)for(;*v&&93!=*v;){62==*v&&++p||60==*v&&--p||43==*v&&++*p||45==*v&&--*p||44==*v&&(*p=getchar())||46==*v&&putchar(*p)||91==*v&&(*p&&main(0,(char**)(--v+2))||(v=(char*)main(-1,(char**)++v)-1));++v;}else for(c=1;c;c+=(91==*v)-(93==*v),++v);return(int)v;}  /*** drpizza@battleaxe.net ***/
Hi,
Sorry, I should have mentioned that I didnt want to do that. I still want to be able to create variables on the stack, but I want to know if it is deleteable.
Private constructors&destructors is too restrictive.
Thanks,
-Adrian
Hi, I am trying to write a class to let me know if the object has been constructed on the heap.

There exist no foolproof method to do this.
See Scott Meyers, More Effective C++, Item #27 for a discussion.

Note - your base class destructor must be virtual.

[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]

[edited by - Fruny on October 13, 2003 10:28:28 PM]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
There exist no foolproof method to do this.

Well I think I''ve figured it out. Other than not working for multiple threads, but thats nothing that a semaphore cant solve.

Let me know if you can spot any issues with this:

static int nHeap; //this will let me know if I am on the heapclass HeapTest {public:	HeapTest() {		printf("0x%x HeapTest alive.\n",this);		if (nHeap>0)			printf("0x%x And I am on the Heap!\n",this);		nHeap--;	}	virtual ~HeapTest() {		printf("0x%x goodbye from HeapTest.\n",this);	}	static void *operator new (size_t size) {		void *memPtr=::operator new(size);		nHeap=1;		return memPtr;	}	static void *operator new[] (size_t size) {		void *memPtr=::operator new(size);		nHeap=(size/sizeof(HeapTest))-1;		return memPtr;	}	virtual void SayHi(char *msg) {		printf("0x%x hi:%s\n",this,msg);	}	int x;};


Output from a test program:

0x12ff6c HeapTest alive.
0x12ff6c hi:ht
0x431b60 HeapTest alive.
0x431b60 And I am on the Heap!
0x431b60 helo from ifht
0x431b60 IFHT:htp
0x431b60 goodbye from InheritFromHeapTest.
0x431b60 goodbye from HeapTest.
0x431b54 HeapTest alive.
0x431b54 And I am on the Heap!
0x431b5c HeapTest alive.
0x431b5c And I am on the Heap!
0x431b64 HeapTest alive.
0x431b64 hi:htpa[2]
0x12ff58 HeapTest alive.
0x12ff58 hi:stackht:I am on the stack
0x431b64 goodbye from HeapTest.
0x431b5c goodbye from HeapTest.
0x431b54 goodbye from HeapTest.
eop
0x12ff58 goodbye from HeapTest.
0x12ff6c goodbye from HeapTest.

Seems to work just fine...

See Scott Meyers, More Effective C++, Item #27 for a discussion.
Dont have the book, a friend told me he used static vector of addresses. But then he wouldnt be able to support the new[] operator..(?) Plus his solution would have more overhead..
Well for a start, a very common feature of memory management is to allocate memory without constructing an object on it, and your class would fail completely under these circumstances

my_class* pmy_memory_pool = my_class.operator new(100*sizeof(my_class)); 


Your code now expects one object to be constructed on the heap
however i could use placement new to construct 100 objects
on the heap, 99 of which would be thought to be on the stack by your code. Also after allocating this memory i could create the next object on the stack, and not create anything on the free-store..

my_class instance_of_my_class_on_the_stack; //o_0

Your code is expecting the constructor for a heap based object, but its getting one on the stack, now when you try to delete the memory occupied by instance_of_my_class_on_the_stack, you will get your undefined behaviour. The same problem exists with operator []. It all comes from the fact that a call to operator new doesnt mean that a constructor will be called after it, but your code relies on just this, and so wont work under anything but the simplest of circumstances
Hey thanks heaps (haha!) for the feedback, I hadn''t thought of that at all!
**SNIP** Infact, I just realised that my code wasn''t working at all. If you call operator new [] with an inherited class, it will have a different class size, **SNIP**

*time passes*
Actually, I have now got, what I believe to be a fully working solution:
#include <vector>using namespace std;class HeapTest {	public:		HeapTest() {			printf("0x%x HeapTest alive.\n",this);			int i;			for (i=0;i<low_address.size();i++) 				if (this>=low_address[i]) {					//printf("0x%x is greater than 0x%x\n",this,low_address);<br></font><br>					<font color=blue>if</font> (<font color=blue>this</font>&lt;high_address[<font color=purple>i</font>]) {<br>						printf(<font color=darkred>"0x%x I am on the heap!\n"</font>,<font color=blue>this</font>);<br>					}<br>				}<br><br>		}<br>		<font color=blue>virtual</font> ~HeapTest() {<br>			printf(<font color=darkred>"0x%x goodbye from HeapTest.\n"</font>,<font color=blue>this</font>);<br>		}<br>		<font color=blue>static</font> <font color=blue>void</font> *<font color=blue>operator</font> <font color=blue>new</font> (size_t size) {<br>			<font color=blue>void</font> *memPtr=::<font color=blue>operator</font> <font color=blue>new</font>(size);<br><br>			low_address.push_back(memPtr);<br>			<br>			<font color=blue>char</font> *bytePtr = (<font color=blue>char</font> *)memPtr;<br>			bytePtr+=size;<br>			high_address.push_back((<font color=blue>void</font> *)bytePtr);<br><br>			<font color=blue>return</font> memPtr;<br>		}<br>		<font color=blue>static</font> <font color=blue>void</font> *<font color=blue>operator</font> <font color=blue>new</font>[] (size_t size) {<br>			<font color=blue>void</font> *memPtr=::<font color=blue>operator</font> <font color=blue>new</font>(size);<br><br>			low_address.push_back(memPtr);<br>			<br>			<font color=blue>char</font> *bytePtr = (<font color=blue>char</font> *)memPtr;<br>			bytePtr+=size;<br>			high_address.push_back((<font color=blue>void</font> *)bytePtr);<br><br>			<font color=blue>return</font> memPtr;<br>		}<br>		<font color=blue>virtual</font> <font color=blue>void</font> SayHi(<font color=blue>char</font> *msg) {<br>			printf(<font color=darkred>"0x%x hi:%s\n"</font>,<font color=blue>this</font>,msg);<br>		}<br>		<font color=blue>int</font> x;<br>		<font color=blue>static</font> vector &lt;<font color=blue>void</font> *&gt; low_address;<br>		<font color=blue>static</font> vector &lt;<font color=blue>void</font> *&gt; high_address;<br>	};<br><br>vector &lt;<font color=blue>void</font> *&gt; HeapTest::low_address;<br>vector &lt;<font color=blue>void</font> *&gt; HeapTest::high_address;<br></pre><!–ENDSCRIPT–><br><br>By using TWO vectors to store the high and low addresses of the memory we have allocated then we can determine if we are &#111;n the heap by searching through the vectors and ensureing we are inbetween the two addresses.<br><br>I dont think there are any flaws in this? Now my solution is slower than Scott Meyers <img src="sad.gif" width=15 height=15 align=middle><br><br>Again I would love to get feedback &#111;n this design.   
quote:Original post by aboeing
Sorry, I should have mentioned that I didnt want to do that.

Too bad.

char a[99999],*p=a;int main(int c,char**V){char*v=c>0?1[V]:(char*)V;if(c>=0)for(;*v&&93!=*v;){62==*v&&++p||60==*v&&--p||43==*v&&++*p||45==*v&&--*p||44==*v&&(*p=getchar())||46==*v&&putchar(*p)||91==*v&&(*p&&main(0,(char**)(--v+2))||(v=(char*)main(-1,(char**)++v)-1));++v;}else for(c=1;c;c+=(91==*v)-(93==*v),++v);return(int)v;}  /*** drpizza@battleaxe.net ***/
Hmm....

Would it help if you make HeapTest a templated class? If you made it HeapTest<class T> (perhaps inherited from a BaseHeapTest so that you can still aggregate them all), and then used it as 'class DerivedClass : public HeapTest<DerivedClass>,' you'd be able to cast 'this' to type T* (and thus get the correct base address of the object).



Superpig
- saving pigs from untimely fates, and when he's not doing that, runs The Binary Refinery.
Enginuity1 | Enginuity2 | Enginuity3 | Enginuity4
ry. .ibu cy. .abu ry. dy. "sy. .ubu py. .ebu ry. py. .ibu gy." fy. .ibu ny. .ebu

[edited by - Superpig on October 14, 2003 1:35:35 PM]

[edited by - Superpig on October 14, 2003 1:36:04 PM]

Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

This topic is closed to new replies.

Advertisement